FLAM

Lookbook

9 Lookbook routes on the FLAM API: The house's looks, newest first (50); Create a look from the director's three choices; One look, whole — selections.

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/lookbook/looks

The house's looks, newest first (50)

Responses

StatusMeaning
200The looks
401No valid session

200 returns:

{
  "looks": [
    {
      "id": "string",
      "organizationId": "string",
      "createdByUserId": "string",
      "brandId": "string",
      "name": "string",
      "modelAssetId": "string",
      "backgroundAssetId": "string",
      "itemAssetIds": [
        {
          "assetId": null,
          "role": null
        }
      ],
      "preset": {
        "aesthetic": "string",
        "background": {}
      },
      "status": "draft",
      "lookBaseAssetId": "string",
      "lookBaseJobId": "string",
      "variations": [
        {}
      ],
      "createdAt": "2026-07-27T09:00:00.000Z",
      "updatedAt": "2026-07-27T09:00:00.000Z"
    }
  ]
}

Call it

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

POST /api/toolkit/lookbook/looks

Create a look from the director's three choices

Model + items + background, dressed by an aesthetic preset. SELECTIONS ONLY — nothing develops and nothing is charged here. Every referenced asset is owner-gated up front, so a foreign id writes nothing.

Request bodyapplication/json (required)

FieldTypeRequiredNotes
namestringyes
modelAssetIdstringyes
itemAssetIdsobject[]yes
backgroundobjectyes{ kind: "preset", value } or { kind: "asset", assetId }
aestheticstringyesA key of the composer's AESTHETIC_PRESETS
brandIdstring | nullno
{
  "name": "string",
  "modelAssetId": "string",
  "itemAssetIds": [
    {
      "assetId": "string",
      "role": "string"
    }
  ],
  "background": {
    "kind": "preset",
    "value": "string",
    "assetId": "string"
  },
  "aesthetic": "string",
  "brandId": "string"
}

Responses

StatusMeaning
201The draft look
400BAD_BODY / NAME_REQUIRED / MODEL_REQUIRED / ITEMS_REQUIRED / TOO_MANY_ITEMS / BAD_ITEM / BAD_BACKGROUND / BAD_AESTHETIC — the rejection carries the legal vocabulary (roles / presets)
401No valid session
404ASSET_NOT_FOUND — a referenced asset is unknown

201 returns:

{
  "look": {
    "id": "string",
    "organizationId": "string",
    "createdByUserId": "string",
    "brandId": "string",
    "name": "string",
    "modelAssetId": "string",
    "backgroundAssetId": "string",
    "itemAssetIds": [
      {
        "assetId": "string",
        "role": "string"
      }
    ],
    "preset": {
      "aesthetic": "string",
      "background": {}
    },
    "status": "draft",
    "lookBaseAssetId": "string",
    "lookBaseJobId": "string",
    "variations": [
      {}
    ],
    "createdAt": "2026-07-27T09:00:00.000Z",
    "updatedAt": "2026-07-27T09:00:00.000Z"
  }
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/lookbook/looks" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"string","modelAssetId":"string","itemAssetIds":[{"assetId":"string","role":"string"}],"background":{"kind":"preset","value":"string","assetId":"string"},"aesthetic":"string","brandId":"string"}'

GET /api/toolkit/lookbook/looks/{id}

One look, whole — selections, lock state, the variation batch

Each variation is joined with its generation_job, so status / resultAssetId / error come back per frame. lockDevelop is the newest lock-stage job, which is what lets a reloaded wizard rehydrate the loop.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Responses

StatusMeaning
200The look + its frames
401No valid session
404NOT_FOUND

200 returns:

{
  "look": {
    "id": "string",
    "organizationId": "string",
    "createdByUserId": "string",
    "brandId": "string",
    "name": "string",
    "modelAssetId": "string",
    "backgroundAssetId": "string",
    "itemAssetIds": [
      {
        "assetId": "string",
        "role": "string"
      }
    ],
    "preset": {
      "aesthetic": "string",
      "background": {}
    },
    "status": "draft",
    "lookBaseAssetId": "string",
    "lookBaseJobId": "string",
    "variations": [
      {}
    ],
    "createdAt": "2026-07-27T09:00:00.000Z",
    "updatedAt": "2026-07-27T09:00:00.000Z"
  },
  "variations": [
    {
      "key": "string",
      "framing": "string",
      "jobId": "string",
      "assetId": "string",
      "locked": true,
      "status": "string",
      "error": "string"
    }
  ],
  "lockDevelop": {
    "id": "string",
    "status": "string",
    "resultAssetId": "string",
    "error": "string"
  }
}

Call it

curl -X GET "https://api.flam.fashion/api/toolkit/lookbook/looks/{id}" \
  -H "Authorization: Bearer $FLAM_API_KEY"

POST /api/toolkit/lookbook/looks/{id}/develop-lock

Develop the LOOK's lock frame

Composes the lock frame server-side (the prompt never enters the jobs DB) and runs it through the spine as ONE job. Draft looks only. Reserves the whole cost — tier plus any restoration pass the destination forces — before anything is enqueued. The finished resultUrl is the frame as DEVELOPED: the tier's pixels at the destination's shape. It is not resampled to output.width × output.height — cut it yourself, which is a pure downscale for every place except the two A-series print sizes.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Request bodyapplication/json

FieldTypeRequiredNotes
quality"1k" | "2k" | "4k"noThe tier the frame develops at — 1,024 / 2,048 / 4,096 px on the long edge.
outputOutputDestinationnoWHERE the frame is going. Omit and the frame simply develops at the tier. A named place carries its own exact pixels; "custom" carries yours. The frame is COMPOSED for that shape and developed at the tier's pixels — the response is not resampled to width × height, so cut it yourself (a pure downscale for every place except the two A-series print sizes, which are composed at 2:3 and cost a 5.7% trim). Never upscale a frame that came back smaller than the place asked for: that is invented detail, and the house sells it only as a priced restoration pass on the 4K tier.
idempotencyKeystringnoMakes one gesture safe to retry. Generated when omitted.
{
  "quality": "2k",
  "output": {
    "destination": "web-tile",
    "width": 0,
    "height": 0
  },
  "idempotencyKey": "string"
}

Responses

StatusMeaning
202Job queued (or the existing job on an idempotent replay)
400BAD_BODY | UNKNOWN_DESTINATION | BAD_DIMENSIONS | OUTPUT_TOO_LARGE | QUALITY_TOO_LOW_FOR_OUTPUT — the body carries a plain-language detail naming the pixels ("1K develops at 1,024 px — an Instagram post needs 1,080.").
401No valid session
402INSUFFICIENT_TOKENS — body includes need + spendable
404NOT_FOUND
409DEVELOP_IN_FLIGHT | LOOK_LOCKED

202 returns:

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

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/lookbook/looks/{id}/develop-lock" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"quality":"2k","output":{"destination":"web-tile","width":0,"height":0},"idempotencyKey":"string"}'

POST /api/toolkit/lookbook/looks/{id}/lock

Freeze the Look Base

PROVENANCE IS REQUIRED — the asset must be the captured result of one of THIS look's develop jobs, otherwise 422. Locking also records lineage: the Look Base's parent becomes the model collage.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Request bodyapplication/json (required)

FieldTypeRequiredNotes
assetIdstringyes
{
  "assetId": "string"
}

Responses

StatusMeaning
200The locked look
400BAD_BODY / ASSET_REQUIRED
401No valid session
404NOT_FOUND
409DEVELOP_IN_FLIGHT — the look is mid-batch
422NOT_A_LOOK_FRAME — that asset did not develop from this look

200 returns:

{
  "look": {
    "id": "string",
    "organizationId": "string",
    "createdByUserId": "string",
    "brandId": "string",
    "name": "string",
    "modelAssetId": "string",
    "backgroundAssetId": "string",
    "itemAssetIds": [
      {
        "assetId": "string",
        "role": "string"
      }
    ],
    "preset": {
      "aesthetic": "string",
      "background": {}
    },
    "status": "draft",
    "lookBaseAssetId": "string",
    "lookBaseJobId": "string",
    "variations": [
      {}
    ],
    "createdAt": "2026-07-27T09:00:00.000Z",
    "updatedAt": "2026-07-27T09:00:00.000Z"
  }
}

Call it

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

POST /api/toolkit/lookbook/looks/{id}/unlock

The explicit intent required before re-developing a locked look

Nothing is destroyed — the look-base fields stay and the next lock supersedes them. Already a draft ⇒ 200, unchanged. No body.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Responses

StatusMeaning
200The draft look
401No valid session
404NOT_FOUND
409DEVELOP_IN_FLIGHT

200 returns:

{
  "look": {
    "id": "string",
    "organizationId": "string",
    "createdByUserId": "string",
    "brandId": "string",
    "name": "string",
    "modelAssetId": "string",
    "backgroundAssetId": "string",
    "itemAssetIds": [
      {
        "assetId": "string",
        "role": "string"
      }
    ],
    "preset": {
      "aesthetic": "string",
      "background": {}
    },
    "status": "draft",
    "lookBaseAssetId": "string",
    "lookBaseJobId": "string",
    "variations": [
      {}
    ],
    "createdAt": "2026-07-27T09:00:00.000Z",
    "updatedAt": "2026-07-27T09:00:00.000Z"
  }
}

Call it

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

POST /api/toolkit/lookbook/looks/{id}/variations

Fan the LOOK out into its variations

One job per chosen variation, with smart refs per the library (a medium shot drops the shoes; a back shot keeps the model). The look must be locked. All-or-nothing reserve across the whole batch: the first shortfall releases every hold already taken. An output destination applies to EVERY frame, so its cost is reserved per frame up front — and every resultUrl comes back as developed (the tier's pixels at that destination's shape), not resampled to its exact width × height.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Request bodyapplication/json (required)

{
  "quality": "2k",
  "output": {
    "destination": "web-tile",
    "width": 0,
    "height": 0
  },
  "idempotencyKey": "string",
  "keys": [
    "string"
  ]
}

Responses

StatusMeaning
202One queued job per requested key
400BAD_BODY | KEYS_REQUIRED | TOO_MANY_FRAMES | UNKNOWN_VARIATION | UNKNOWN_DESTINATION | BAD_DIMENSIONS | OUTPUT_TOO_LARGE | QUALITY_TOO_LOW_FOR_OUTPUT
401No valid session
402INSUFFICIENT_TOKENS — body includes need + spendable
404NOT_FOUND
409DEVELOP_IN_FLIGHT | LOCK_REQUIRED

202 returns:

{
  "jobs": [
    {
      "key": "string",
      "jobId": "string"
    }
  ]
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/lookbook/looks/{id}/variations" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"quality":"2k","output":{"destination":"web-tile","width":0,"height":0},"idempotencyKey":"string","keys":["string"]}'

GET /api/toolkit/lookbook/templates

Shot templates — the house's built-ins plus the caller's own

Built-ins are the rows with organization_id IS NULL; they list first.

Responses

StatusMeaning
200The templates, each with its shot count
401No valid session

200 returns:

{
  "templates": [
    {
      "id": "string",
      "name": "string",
      "slots": {},
      "shot_count": 0,
      "created_at": "2026-07-27T09:00:00.000Z",
      "updated_at": "2026-07-27T09:00:00.000Z"
    }
  ]
}

Call it

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

GET /api/toolkit/lookbook/templates/{id}

One template and its shots, in order

Parameters

InNameTypeRequiredNotes
pathidstringyes

Responses

StatusMeaning
200The template + its shots
400id required
401No valid session
404NOT_FOUND — unknown

200 returns:

{
  "template": {},
  "shots": [
    {
      "id": "string",
      "type": "string",
      "framing": "string",
      "pose": "string",
      "expression": "string",
      "sort_order": 0
    }
  ]
}

Call it

curl -X GET "https://api.flam.fashion/api/toolkit/lookbook/templates/{id}" \
  -H "Authorization: Bearer $FLAM_API_KEY"