FLAM

Jobs

7 Jobs routes on the FLAM API: The house's develop RUNS, newest first; Stop a whole run — honestly; Reconnect backfill — jobs newer than an eventSeq.

Base URL https://api.flam.fashion. Send Authorization: Bearer flam_sk_… on every call; a handful of routes are session-only and say so. How keys and roles work.

GET /api/toolkit/develops

The house's develop RUNS, newest first

A run is DERIVED, not stored — params.runId, else the lookbook batch (lookId + stage), else same tool within a time gap. state=active keeps only runs with a frame still queued or rendering; the counts stay WHOLE (a 2-of-5 run still reports 5).

Parameters

InNameTypeRequiredNotes
querystate"active" | "all"no
querylimitintegerno

Responses

StatusMeaning
200The runs
400BAD_STATE / BAD_LIMIT
401No valid session

200 returns:

{
  "runs": [
    {
      "id": "string",
      "kind": "lookbook",
      "tool": "string",
      "title": "string",
      "stage": "lock",
      "lookId": "string",
      "state": "queued",
      "counts": {
        "total": 0,
        "done": 0,
        "failed": 0,
        "cancelled": 0,
        "active": 0
      },
      "createdAt": "2026-07-27T09:00:00.000Z",
      "finishedAt": "2026-07-27T09:00:00.000Z",
      "thumbAssetId": "string",
      "jobIds": [
        "string"
      ],
      "againCost": 0
    }
  ]
}

Call it

curl -X GET "https://api.flam.fashion/api/toolkit/develops" \
  -H "Authorization: Bearer $FLAM_API_KEY"

POST /api/toolkit/develops/{id}/cancel

Stop a whole run — honestly

A frame still QUEUED is cancelled and its hold RELEASED. A frame already RENDERING finishes and still costs: the model call is paid for and cannot be aborted. The response says both numbers out loud rather than implying a refund that is not happening.

Parameters

InNameTypeRequiredNotes
pathidstringyesA develop-run id (run.<uuid> / look.<uuid>.<stage>)

Responses

StatusMeaning
200What actually stopped
401No valid session
404Unknown

200 returns:

{
  "id": "string",
  "cancelled": 0,
  "cancelledJobIds": [
    "string"
  ],
  "stillRendering": 0,
  "tokensReleased": 0,
  "run": {
    "id": "string",
    "kind": "lookbook",
    "tool": "string",
    "title": "string",
    "stage": "lock",
    "lookId": "string",
    "state": "queued",
    "counts": {
      "total": 0,
      "done": 0,
      "failed": 0,
      "cancelled": 0,
      "active": 0
    },
    "createdAt": "2026-07-27T09:00:00.000Z",
    "finishedAt": "2026-07-27T09:00:00.000Z",
    "thumbAssetId": "string",
    "jobIds": [
      "string"
    ],
    "againCost": 0
  }
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/develops/{id}/cancel" \
  -H "Authorization: Bearer $FLAM_API_KEY"

GET /api/toolkit/jobs

Reconnect backfill — jobs newer than an eventSeq cursor

The session user's generation_jobs with event_seq > since, ascending, in the same Job shape the WebSocket pushes. The web client calls this on every WS (re)open with its max seen eventSeq so events missed while the socket was down replay. since=0 returns everything.

Parameters

InNameTypeRequiredNotes
querysinceintegerno

Responses

StatusMeaning
200Missed job events, ascending by eventSeq
400BAD_SINCE
401No valid session

200 returns:

{
  "jobs": [
    {
      "id": "string",
      "tool": "string",
      "status": "queued",
      "resultUrl": "string",
      "resultAssetId": "string",
      "error": "string",
      "brandId": "string",
      "createdAt": "2026-07-27T09:00:00.000Z",
      "eventSeq": 0
    }
  ]
}

Call it

curl -X GET "https://api.flam.fashion/api/toolkit/jobs" \
  -H "Authorization: Bearer $FLAM_API_KEY"

POST /api/toolkit/jobs/{id}/cancel

Stop ONE frame

Same honesty as the run-level stop. cancelled: false carries a reason — ALREADY_RENDERING (paid for, still coming) or ALREADY_SETTLED.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Responses

StatusMeaning
200The frame's true state
401No valid session
404NOT_FOUND — unknown or foreign (never a 403 that leaks existence)

200 returns:

{
  "id": "string",
  "cancelled": true,
  "state": "queued",
  "tokensReleased": 0,
  "reason": "ALREADY_RENDERING"
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/jobs/{id}/cancel" \
  -H "Authorization: Bearer $FLAM_API_KEY"

POST /api/toolkit/jobs/{id}/duplicate

Run the same frame again as a NEW job

A fresh reserve, and that is correct — it is a new model call. Params are copied VERBATIM including the destination, so the quote covers any pass that destination forces. The source job is never re-billed or mutated.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Request bodyapplication/json

FieldTypeRequiredNotes
idempotencyKeystringnoMakes one gesture safe to retry. Without it a double-click reserves twice and enqueues two paid model calls.
{
  "idempotencyKey": "string"
}

Responses

StatusMeaning
202Queued
401No valid session
402INSUFFICIENT_TOKENS { need, spendable } — nothing created, nothing charged
403A viewer may not spend the house's tokens
404NOT_FOUND
422UNKNOWN_TOOL / NO_INPUTS — never charged for a job that cannot run

202 returns:

{
  "jobId": "string",
  "status": "queued",
  "cost": 0
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/jobs/{id}/duplicate" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"idempotencyKey":"string"}'

POST /api/toolkit/jobs/submit

Submit an async generation job

Generic over tools and N named images: tool must be a known async slug; every file field stages as a named input (field name = role, e.g. try-on sends model + garment); other string fields become the tool's params. Reserves a token hold before enqueue; idempotent on (user, idempotencyKey). Progress is pushed over the jobs WebSocket.

Request bodymultipart/form-data (required)

FieldTypeRequiredNotes
toolstringyesOne of ASYNC_TOOL_SLUGS (upscale, reframe, generate-character, packshot, camera-lens, harmonize, tryon, iron, cleanup, color-change, fit-adjust, lookbook-video)
idempotencyKeystringno
brandIdstringno
imagefilenoSingle-image tools; multi-input tools use per-role field names instead

Responses

StatusMeaning
202Job queued (or the existing job on an idempotent replay)
400UNKNOWN_TOOL | NO_IMAGE | EMPTY_IMAGE | BAD_FORM
401No valid session
402INSUFFICIENT_TOKENS — body includes need + spendable
413IMAGE_TOO_LARGE (12 MB per image)
500SUBMIT_FAILED

202 returns:

{
  "jobId": "string",
  "status": "string"
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/jobs/submit" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -F "tool=<tool>" \
  -F "idempotencyKey=<idempotencyKey>" \
  -F "brandId=<brandId>" \
  -F "image=@image.png"

GET /api/toolkit/jobs/ws

Job event stream (WebSocket upgrade)

Upgrades to a WebSocket forwarded to the caller's per-user JobStreamDO. Every job status change is pushed as {"type":"job","job":Job}. Requires the session cookie on the handshake.

Responses

StatusMeaning
101Switching Protocols — socket attached
401No valid session
426Upgrade header missing — not a WebSocket request

Call it

curl -X GET "https://api.flam.fashion/api/toolkit/jobs/ws" \
  -H "Authorization: Bearer $FLAM_API_KEY"