Pixly Developer Docs
Everything Pixly's app can generate — virtual staging, photo enhancement, cinematic video — is callable by AI agents and scripts through one MCP endpoint, with an OAuth sign-in flow and a CLI on top. This page is the complete reference; the machine-readable mirror lives at /docs.md.
Getting started
The fastest path, end to end:
- Create an API key in Settings → API keys (shown once — copy it immediately). An active Pixly subscription is required.
- Connect a client (one-step setup per client) or install the CLI:
npx pixly-cli help - Ask for a staged room — or run
pixly stage photo.jpg --style scandinavian
Generations spend the account's normal credit balance — same pipeline, quality, and pricing as the app, and every result also appears in the Pixly Library.
Authentication
Two credential types, both sent as Authorization: Bearer <token>:
- API keys (pixly_sk_…) — for Claude Code, Cursor, scripts, and CI. Created in Settings, stored hashed, revocable instantly. Keys can never mint other keys.
- OAuth 2.1 (pixly_at_… access tokens) — for clients with a sign-in flow, like claude.ai custom connectors. Public clients with PKCE S256 only; single-use authorization codes; refresh-token rotation.
OAuth surface (discovery starts from the 401 WWW-Authenticate header):
| Parameter | Type | Required | Description |
|---|---|---|---|
| GET /.well-known/oauth-authorization-server | — | no | RFC 8414 authorization-server metadata (endpoints, PKCE methods, grant types) |
| GET /.well-known/oauth-protected-resource/api/mcp | — | no | RFC 9728 protected-resource metadata — advertised in the 401 WWW-Authenticate header |
| POST /api/oauth/register | — | no | RFC 7591 dynamic client registration (open; public clients; body: client_name, redirect_uris[]) |
| GET /oauth/authorize | — | no | User consent page — query: response_type=code, client_id, redirect_uri, state, code_challenge, code_challenge_method=S256 |
| POST /api/oauth/token | — | no | Token endpoint — grant_type authorization_code (code, code_verifier, client_id, redirect_uri) or refresh_token (rotates the pair). Form-encoded or JSON. |
MCP endpoint
POST https://pixly.app/api/mcp
Model Context Protocol over streamable HTTP, stateless: single JSON responses, no SSE streams, no sessions. Protocol versions 2025-03-26 and 2025-06-18. Methods: initialize, tools/list, tools/call, ping; notifications return 202.
Example request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "virtual_staging",
"arguments": {
"r2Path": "users/<your-user-id>/staging/1754820000000-living-room.jpg",
"style": "scandinavian",
"numImages": 1
}
}
}Example response — result.content[0].text is JSON (a job snapshot); tool failures set result.isError: true with an actionable message:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"jobId\": \"4bf4bb95-f5af-44d8-9508-65807f68636d\",\n \"type\": \"image_staging\",\n \"status\": \"processing\",\n \"creditsCharged\": 1,\n \"createdAt\": \"2026-08-10T20:07:08Z\",\n \"note\": \"Still running — call get_job again in a few seconds.\"\n}"
}
]
}
}Typical workflow
- Get the photo in: create_upload_ticket (local bytes — PUT to the returned uploadUrl) or upload_image_from_url. Both return an r2Path.
- Call a generation tool with that r2Path → job snapshot.
- Poll get_job until status: "completed" → download resultUrls. Image tools usually complete inline (up to ~2 min); videos take 1–5 minutes.
Tools reference
Generated from the live tool registry — this is exactly what tools/list returns.
virtual_staging
Furnish and style an empty (or badly furnished) room photo in a chosen interior style. The flagship tool. HD, watermark-free results. Costs credits from the user's Pixly balance.
| Parameter | Type | Required | Description |
|---|---|---|---|
| r2Path | string | yes | R2 object path of the uploaded source photo (from an upload-url ticket) |
| style | string | yes | Style id from the Pixly styles catalog (lib/real-estate/styles.ts) |
| roomType | string | no | Room type hint; omit to let vision infer it from the photo |
| customInstructions | string | no | Optional extra instructions blended into the staging prompt |
| numImages | integer (default: 1) | yes | Variations to generate (1-4) |
| stagingQuality | "standard" | "pro" | no | pro = higher-quality 2K tier at 2 credits/image |
edit_staged_photo
Apply a specific change to a previously staged photo (swap the sofa, add a rug) while preserving everything else. Costs credits from the user's Pixly balance.
| Parameter | Type | Required | Description |
|---|---|---|---|
| r2Path | string | yes | R2 path of the PREVIOUS staged result to edit |
| style | string | yes | Style id, used as context to match new elements |
| customInstructions | string | yes | The concrete edit to apply, e.g. "change the sofa to grey" |
| sourceJobId | string | no | Job the edited result came from (lineage) |
| sourceResultIndex | integer | no | Which variation was edited |
cinematic_motion
Turn a single listing photo into a short cinematic clip with a professional camera move (zoom, orbit, fly-through, crane up…). No second frame needed. Costs credits from the user's Pixly balance.
| Parameter | Type | Required | Description |
|---|---|---|---|
| r2Path | string | yes | R2 object path of the uploaded source photo (from an upload-url ticket) |
| cameraMove | "zoom" | "fly-through" | "orbit" | "pan" | "aerial" | "pull-back" | "crane-up" | "tilt-up" | "boom-down" | "drone-orbit" | "flyover" | "pull-away" | "handheld" | yes | Camera-move preset |
| durationSeconds | 5 | 10 | yes | Clip length; the routed model validates its supported tiers |
| format | "9:16" | "16:9" | yes | Aspect ratio |
before_after_reel
Animate a before→after transformation (e.g. empty room → staged) into a social-ready reveal video from two frames. Costs credits from the user's Pixly balance.
| Parameter | Type | Required | Description |
|---|---|---|---|
| beforeR2Path | string | yes | Start frame (e.g. the empty room photo) |
| afterR2Path | string | yes | End frame (e.g. the staged result) |
| videoIntent | "staging_reveal" | "staging_reveal_reverse" | "construction_timelapse" | yes | |
| revealStyle | "smooth" | "slideIn" | "dropLand" | "glowBuild" | "movers" | yes | How the transformation is revealed |
| durationSeconds | 5 | 10 | yes | |
| format | "9:16" | "16:9" | "1:1" | yes |
enhance_photo
Turn an amateur listing photo into a finished, professional real-estate photo (exposure, color, clarity). Returns 3 variants to pick from. Costs credits from the user's Pixly balance.
| Parameter | Type | Required | Description |
|---|---|---|---|
| r2Path | string | yes | R2 object path of the uploaded source photo (from an upload-url ticket) |
declutter_photo
Remove clutter, mess, and personal items from a listing photo while keeping the room, furniture, and architecture intact. Costs credits from the user's Pixly balance.
| Parameter | Type | Required | Description |
|---|---|---|---|
| r2Path | string | yes | R2 object path of the uploaded source photo (from an upload-url ticket) |
day_to_night
Turn a daytime listing photo into a magazine-style night scene — lights on, warm glow, dusk sky — with the building and camera angle unchanged. Costs credits from the user's Pixly balance.
| Parameter | Type | Required | Description |
|---|---|---|---|
| r2Path | string | yes | R2 object path of the uploaded source photo (from an upload-url ticket) |
plot_sign
Place a photorealistic 3D monument sign with your exact text (price, area, SOLD) onto a photo of an empty plot, matched to perspective and lighting. Costs credits from the user's Pixly balance.
| Parameter | Type | Required | Description |
|---|---|---|---|
| r2Path | string | yes | R2 object path of the uploaded source photo (from an upload-url ticket) |
| text | string | yes | Sign label, verbatim — a price ("$1,200,000"), area ("800 m²"), or short word ("SOLD") |
| look | "stone" | "metal" | "grass" | "sign" | no | Sign material/style; defaults server-side |
| orientation | "standing" | "lying" | no | Letter orientation (letter looks only); defaults server-side |
upload_image_from_url
Import an image into Pixly from a URL. Returns the r2Path that every generation tool takes as its photo input. Use this first when the photo isn't already in Pixly.
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | yes | Publicly reachable image URL (JPG/PNG/WebP, max 10 MB) to import into Pixly |
create_upload_ticket
Get a presigned upload URL for a LOCAL image file (when you have bytes, not a URL). PUT the file to uploadUrl with the same Content-Type, then pass the returned r2Path to a generation tool. Ticket expires in 1 hour.
| Parameter | Type | Required | Description |
|---|---|---|---|
| filename | string | yes | Original filename, used for the extension |
| contentType | "image/jpeg" | "image/png" | "image/webp" | yes | MIME type the PUT request will send |
get_job
Check a generation job's status and fetch result URLs when it's done. Video jobs take 1-5 minutes — poll this with a few seconds between calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
| jobId | string | yes | Job id returned by a generation tool |
list_library
List the user's recent generations (newest first).
| Parameter | Type | Required | Description |
|---|---|---|---|
| type | "images" | "videos" | no | Filter by media type |
| limit | integer (default: 20) | yes |
get_credit_balance
The user's current Pixly credit balance and plan.
Jobs & results
Every generation tool and get_job return the same snapshot shape:
| Parameter | Type | Required | Description |
|---|---|---|---|
| jobId | string | yes | Job id — pass to get_job to poll |
| type | "image_staging" | "video_pair" | "video_tour" | yes | Job family |
| status | "pending" | "processing" | "completed" | "failed" | yes | Anything not completed/failed is still running |
| creditsCharged | number | yes | Credits debited for this job |
| createdAt | string (ISO 8601) | yes | Submission time |
| completedAt | string (ISO 8601) | no | Present once finished |
| resultUrls | string[] | no | Presigned download URLs (present when completed; valid 7 days) |
| error | string | no | Failure reason (failed jobs enter the 24h credit-refund review automatically) |
| note | string | no | Hint for agents, e.g. poll again in a few seconds |
Result URLs are presigned and valid for 7 days; the generation itself stays in the user's Pixly Library permanently.
Errors & refunds
- HTTP 401 — missing or invalid credential. The WWW-Authenticate header carries the OAuth resource-metadata URL so OAuth-capable clients discover the sign-in flow automatically.
- JSON-RPC errors — -32700 parse, -32600 invalid request (batching is not supported), -32601 unknown method, -32602 invalid params, -32603 internal.
- Tool errors — result.isError: true with a message agents can act on: insufficient credits (with the amount needed), invalid input (with the exact field), or a source photo that's no longer available.
- Refunds — a job that fails after the charge enters refund review automatically; credits return within 24 hours. No action needed.
CLI
npx pixly-cli help # or: npm install -g pixly-cli export PIXLY_API_KEY=pixly_sk_...
Node ≥ 18, zero dependencies. Local photo arguments upload automatically; results download to the current directory (--out to choose a path; multiple variations get -1, -2, … suffixes).
pixly stage <photo> --style <id>
Virtually stage a room photo. Local files upload automatically.
- --style <id> (required) — style id, e.g. scandinavian, modern-luxury
- --room <type> — room-type hint; omitted = inferred from the photo
- --instructions <text> — extra instructions blended into the prompt
- --variations <1-4> — number of variations (default 1)
- --pro — 2K quality tier (2 credits/image)
- --out <file.jpg> — output path (default: <name>-staged.jpg)
pixly enhance <photo>
Professional photo enhancement — returns 3 variants.
- --out <file.jpg>
pixly declutter <photo>
Remove clutter and personal items.
- --out <file.jpg>
pixly day-to-night <photo>
Turn a daytime photo into a night scene.
- --out <file.jpg>
pixly plot-sign <photo> --text "SOLD"
Photorealistic 3D monument sign on an empty-plot photo.
- --text <label> (required, max 24 chars)
- --look stone|metal|grass|sign
- --orientation standing|lying
- --out <file.jpg>
pixly motion <photo>
Cinematic camera-move clip from a single photo.
- --move zoom|fly-through|orbit|pan|aerial|pull-back|crane-up|tilt-up|boom-down|drone-orbit|flyover|pull-away|handheld
- --duration 5|10 (default 5)
- --format 9:16|16:9 (default 9:16)
- --out <file.mp4>
pixly reel --before <a.jpg> --after <b.jpg>
Before→after reveal video from two frames.
- --intent staging_reveal|staging_reveal_reverse|construction_timelapse
- --reveal smooth|slideIn|dropLand|glowBuild|movers (default smooth)
- --duration 5|10 · --format 9:16|16:9|1:1 · --out <file.mp4>
pixly job <jobId>
Job status + result URLs (JSON).
pixly jobs
Recent generations.
- --limit <n> (default 20)
- --type images|videos
pixly balance
Credits remaining and plan.
pixly tools
List every available tool.
For AI agents
This entire reference is available as pure markdown at https://pixly.app/docs.md (also linked from /llms.txt). To teach an agent Pixly in one message, paste this:
Connect to Pixly (real-estate photo & video AI) via MCP: - Endpoint: https://pixly.app/api/mcp (streamable HTTP, stateless JSON responses) - Auth: header "Authorization: Bearer pixly_sk_..." (user creates the key at https://pixly.app/app/settings) or OAuth sign-in - Workflow: create_upload_ticket (local file) or upload_image_from_url → returns r2Path → call a generation tool (virtual_staging, enhance_photo, declutter_photo, day_to_night, plot_sign, cinematic_motion, before_after_reel, edit_staged_photo) → poll get_job until status "completed" → download resultUrls. - Generations spend the user's Pixly credit balance (check with get_credit_balance). Failed jobs auto-refund within 24h. - Full reference: https://pixly.app/docs.md