API Documentation

PhotoFig API

Queue product image edits, upload source files, poll job status, and receive webhook events with API keys.

Need a machine-readable schema? Download the OpenAPI JSON.

On this page
QuickstartAsync
export PHOTOFIG_API_KEY="pf_live_..."

JOB_ID=$(curl -s -X POST https://api.photofig.com/v1/tools/ai-shadows/edit \
  -H "Authorization: Bearer $PHOTOFIG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "imageUrl": "https://cdn.example.com/product.png",
    "parameters": { "seed": 128742 }
  }' | jq -r '.id')

curl -L "$(curl -s -H "Authorization: Bearer $PHOTOFIG_API_KEY" \
  "https://api.photofig.com/v1/jobs/$JOB_ID" \
  | tee /dev/stderr \
  | jq -r '.resultUrl')" \
  -o ./photofig-result.png

Authentication

API routes accept API keys generated from Settings. The full secret is shown only once when the key is created.

Authorization: Bearer pf_live_...

Account

GET/v1/account

Use this route as a credential smoke test and to inspect plan limits, remaining credits, and available tools.

{
  "accountId": "uuid",
  "workspaceId": "uuid",
  "plan": "pro",
  "apiEnabled": true,
  "credits": {
    "remaining": 1200,
    "periodEndsAt": "2026-08-01T00:00:00Z"
  },
  "limits": {
    "maxFileSizeBytes": 25000000,
    "rateLimitPerHour": 500,
    "concurrentJobs": 5,
    "maxApiKeys": 3
  },
  "tools": ["mask", "ai-bg", "ai-shadows"]
}

Tools

GET/v1/tools
GET/v1/tools/{tool_id}

Each tool accepts a normal product image through imageUrl, uploadSessionId, or multipart upload. You do not need to call mask first unless you specifically want a transparent cutout.

mask

Removes the background and returns a PNG subject cutout with transparency. Use it when you need the product isolated for your own compositing or marketplace workflow.

ai-shadows

Adds a realistic product shadow and returns an image ready to place on a clean background. Works best on product photos where the subject is clearly visible and not hidden by a busy scene.

ai-bg

Generates a new product background from a short theme, such as warm oak tabletop or minimal white studio. Submit the product image directly; a separate background-removal call is not required.

Upload and Edit in One Call

POST/v1/tools/{tool_id}/edit

This is the simplest path: submit an edit with an imageUrl when the source image is already reachable by URL, or send a multipart image field for a quick local-file request. It is best when you only need to run one tool against one input.

Choose this when you have one image, one edit, and no need to reuse the uploaded source.

curl -X POST https://api.photofig.com/v1/tools/ai-shadows/edit \
  -H "Authorization: Bearer pf_live_..." \
  -F "[email protected]" \
  -F "seed=128742"

Upload First, Edit Later

POST/v1/uploads

Create an upload session when the source is a larger local file, or when you want to run more than one tool against the same input. The image bytes go straight to storage once; later edit requests only send the returned uploadSessionId.

Choose this when upload size matters, or when the same source image will feed multiple tools or retries.

UPLOAD_JSON=$(curl -s -X POST https://api.photofig.com/v1/uploads \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "contentType": "image/jpeg",
    "filename": "product.jpg",
    "sizeBytes": 1842031
  }')

curl -X PUT "$(echo "$UPLOAD_JSON" | jq -r '.uploadUrl')" \
  -H "Content-Type: image/jpeg" \
  --data-binary @product.jpg

curl -X POST https://api.photofig.com/v1/tools/ai-shadows/edit \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d "{
    \"uploadSessionId\": \"$(echo "$UPLOAD_JSON" | jq -r '.uploadSessionId')\",
    \"parameters\": { \"seed\": 128742 }
  }"

Create Edits

POST/v1/tools/{tool_id}/edit

Submit edits with imageUrl, uploadSessionId, or multipart field image. The response returns a job ID you can use for polling, cancellation, deletion, and webhook correlation.

AI Background

The AI background endpoint works best with a concise scene theme: a setting, surface, lighting cue, or mood in a few words. Good examples include warm oak tabletop,minimal white studio, and soft morning kitchen light.

curl -X POST https://api.photofig.com/v1/tools/ai-bg/edit \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "imageUrl": "https://cdn.example.com/product.png",
    "parameters": {
      "theme": "warm oak tabletop",
      "seed": 128742
    }
  }'

Product Shadow

curl -X POST https://api.photofig.com/v1/tools/ai-shadows/edit \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "imageUrl": "https://cdn.example.com/product.png",
    "parameters": { "seed": 128742 }
  }'

Mask

curl -X POST https://api.photofig.com/v1/tools/mask/edit \
  -H "Authorization: Bearer pf_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "imageUrl": "https://cdn.example.com/product.png",
    "parameters": { "mode": "standard" }
  }'

Jobs

GET/v1/jobs
GET/v1/jobs/{id}
POST/v1/jobs/{id}/cancel
DELETE/v1/jobs/{id}

List, poll, cancel, or delete API jobs. API jobs do not appear in logged-in Uploads or Designs, but deletion removes API-owned artifacts.

GET /v1/jobs/{id} returns JSON status metadata. When processing is complete, download the image from resultUrl. Polling is the simplest integration path; webhooks are better when your backend should be notified automatically.

{
  "id": "api_edit_uuid",
  "tool": "ai-shadows",
  "status": "completed",
  "createdAt": "2026-07-15T10:15:00Z",
  "startedAt": "2026-07-15T10:15:04Z",
  "finishedAt": "2026-07-15T10:15:23Z",
  "statusUrl": "https://api.photofig.com/v1/jobs/api_edit_uuid",
  "resultUrl": "https://storage.example.com/signed-result-url",
  "thumbnailUrl": "https://storage.example.com/signed-thumbnail-url",
  "expiresAt": "2026-07-15T11:15:23Z",
  "error": null
}

Webhooks

Webhooks are callbacks from PhotoFig to your server. Register an HTTPS URL, and PhotoFig sends a request when an API job finishes, fails, or is cancelled. This lets your backend react immediately instead of polling GET /v1/jobs/{id} until the status changes.

GET/v1/webhooks
POST/v1/webhooks
PATCH/v1/webhooks/{webhook_id}
DELETE/v1/webhooks/{webhook_id}
POST/v1/webhooks/{webhook_id}/test

Supported events are webhook.test,job.completed,job.failed, and job.cancelled. Event payloads use the same job ID returned when the edit is created.

{
  "event": "job.completed",
  "eventId": "uuid",
  "createdAt": "2026-07-15T10:15:23Z",
  "data": {
    "id": "api_edit_uuid",
    "tool": "ai-shadows",
    "status": "completed",
    "resultUrl": "https://storage.example.com/signed-result-url",
    "thumbnailUrl": "https://storage.example.com/signed-thumbnail-url"
  }
}

Errors

Error responses use a stable machine-readable code and a human-readable message. Treat the code as the value to branch on in client logic.

{
  "code": "invalid_request",
  "message": "imageUrl or uploadSessionId is required"
}

Result Lifecycle

Completed API jobs stay visible through GET /v1/jobs and GET /v1/jobs/{id}. The status response contains signed resultUrl and thumbnailUrl links when outputs are available.

API artifacts are stored separately from interactive uploads and designs. They are not shown in the app library.

API job records and API-owned input, result, and thumbnail files are retained for 1 hour after the job reaches completed, failed, or cancelled. The expiresAt field shows the scheduled cleanup time for finished jobs.

Use DELETE /v1/jobs/{id} after downloading results you no longer need. It cancels unfinished work when possible and deletes the job record plus API-owned artifacts before the automatic cleanup window.