FLAM

Brand

7 Brand routes on the FLAM API: The house's extracted visual DNA; Correct the extracted DNA, or switch the injection off; Read the house's visual DNA off.

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/brand

The house's extracted visual DNA

Returns the profile plus clause — the EXACT text the generators will inject, built by the same @flam/ai builder the worker calls, so this review surface can never disagree with the renders. A house that has never run a read gets empty defaults, never a 404.

Responses

StatusMeaning
200The profile
401Unauthorized

200 returns:

{
  "ok": true,
  "profile": {
    "visionEn": "string",
    "vibe": [
      "string"
    ],
    "palette": [
      "string"
    ],
    "signatureKeywords": [
      {
        "category": "string",
        "phrase": "string"
      }
    ],
    "injectEnabled": true,
    "dnaRuns": 0,
    "clause": "string"
  }
}

Call it

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

PATCH /api/toolkit/brand

Correct the extracted DNA, or switch the injection off

The house gets the last word. Every field is optional and merges onto what is stored; the merged result is then clamped exactly as an extraction would be, so a partial edit cannot smuggle past a bound the extractor enforces. injectEnabled: false is the kill switch — the profile stays, the clause goes empty, every generator renders unbranded. dna_runs is never touched here, so editing is free and unlimited.

Request bodyapplication/json (required)

FieldTypeRequiredNotes
visionEnstringno
vibestring[]no
palettestring[]no
signatureKeywordsobject[]no
injectEnabledbooleanno
{
  "visionEn": "string",
  "vibe": [
    "string"
  ],
  "palette": [
    "string"
  ],
  "signatureKeywords": [
    {
      "category": "string",
      "phrase": "string"
    }
  ],
  "injectEnabled": true
}

Responses

StatusMeaning
200The updated profile
400BAD_JSON
401Unauthorized
403A viewer may not edit the clause every render pays for

200 returns:

{
  "ok": true,
  "profile": {
    "visionEn": "string",
    "vibe": [
      "string"
    ],
    "palette": [
      "string"
    ],
    "signatureKeywords": [
      {
        "category": "string",
        "phrase": "string"
      }
    ],
    "injectEnabled": true,
    "dnaRuns": 0,
    "clause": "string"
  }
}

Call it

curl -X PATCH "https://api.flam.fashion/api/toolkit/brand" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"visionEn":"string","vibe":["string"],"palette":["string"],"signatureKeywords":[{"category":"string","phrase":"string"}],"injectEnabled":true}'

POST /api/toolkit/brand/dna

Read the house's visual DNA off its own imagery

The cross-tool magic. Upload 1-10 of the house's own previous frames and one multi-image vision call finds the through-line: vibe, palette, signature cues and a visionEn paragraph that is injected into EVERY packshot, character and lookbook frame the house develops afterwards. The read describes the house faithfully — the 8-category fashion vocabulary is a descriptive lens, never a style to impose. BILLING is server-decided from brand_profiles.dna_runs and never from the client: the FIRST read per house is free (charged 0), every re-read costs 2 tokens. The counter is bumped on every successful read whether or not the house keeps the answer. A failed read charges nothing and does not bump it.

Request bodymultipart/form-data (required)

FieldTypeRequiredNotes
imagefile[]yes
vibestringnoJSON string[] of vibe hints
productsstringnowhat the house sells

Responses

StatusMeaning
200The extracted (and stored) DNA
400NO_IMAGES / BAD_FORM
401Unauthorized
402INSUFFICIENT_TOKENS / AI_NEEDS_BILLING
403A viewer may not spend the house's tokens
413IMAGE_TOO_LARGE / SET_TOO_LARGE (32MB across the set)
502AI_BAD_OUTPUT / AI_FAILED
503AI_NOT_CONFIGURED

200 returns:

{
  "ok": true,
  "profile": {
    "visionEn": "string",
    "vibe": [
      "string"
    ],
    "palette": [
      "string"
    ],
    "signatureKeywords": [
      {
        "category": "string",
        "phrase": "string"
      }
    ],
    "injectEnabled": true,
    "dnaRuns": 0,
    "clause": "string"
  },
  "charged": 0
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/brand/dna" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -F "image=<image>" \
  -F "vibe=<vibe>" \
  -F "products=<products>"

GET /api/toolkit/brand/marks

The house's logo renditions, and which set rides a frame

Brand DNA is prose; this is the pixels. A rendition is the house's mark as it really appears on a material — embossed on calf, woven into a label, foil on a swing tag — and one or two of them ride a lookbook frame as reference images.

WHY IT MATTERS: the judge has carried mark-wrong (weight 20) since the rubric was written, with a predicate ending "or is not the mark in the reference". There was no mark in the reference. A comparison against nothing is not a lenient check, it is a guaranteed pass — so until a house registers a rendition, that code is now withdrawn rather than asked unanswerably (rubric v6).

Pass collectionId to see a drop's own set beside the house's. The override answers WHOLE, never merged: a capsule that foil-stamps where the main line embosses must not be shown both finishes. riding says which of the two a frame developed there will actually carry.

Parameters

InNameTypeRequiredNotes
querycollectionIdstringnoA collection (asset folder) id. Unknown or another house's is 404.

Responses

StatusMeaning
200The house's renditions, the collection's override, and which rides
401Unauthorized
404No such collection in this house

200 returns:

{
  "house": [
    {
      "id": "string",
      "assetId": "string",
      "collectionId": "string",
      "material": "string",
      "placement": "string",
      "note": "string",
      "sortOrder": 0,
      "filename": "string",
      "createdAt": "2026-07-27T09:00:00.000Z"
    }
  ],
  "collection": [
    {
      "id": "string",
      "assetId": "string",
      "collectionId": "string",
      "material": "string",
      "placement": "string",
      "note": "string",
      "sortOrder": 0,
      "filename": "string",
      "createdAt": "2026-07-27T09:00:00.000Z"
    }
  ],
  "riding": "house"
}

Call it

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

POST /api/toolkit/brand/marks

Register an uploaded image as a rendition of the mark

THERE IS NO UPLOAD HERE. The bytes go through POST /api/toolkit/assets/upload with kind=mark like every other image, and this route registers the resulting asset id. A second bytes-door would be the third implementation of "store bytes then insert an assets row" in this codebase.

material is REQUIRED and it is not decoration — it rides the generation prompt verbatim ("embossed on black calf"), and it is the question that catches a wrong upload: a house about to register a screenshot of its website header discovers, while trying to name the material, that it has not got what we asked for.

Omit collectionId for the house's own set; send one to create that drop's override. Nothing is charged.

Request bodyapplication/json (required)

FieldTypeRequiredNotes
assetIdstringyesAn asset owned by THIS house. Re-read under the actor's org.
materialstringyesWhat it is on — "embossed on black calf". Rides the prompt verbatim.
placementstringnoWhere on the piece — "left chest". Rides the sentence; not yet verified by the judge.
notestringno
sortOrderintegernoWhich renditions ride. At most two travel with a frame, lowest first.
collectionIdstringnoOmit for the house's set. Set to override that collection whole.
{
  "assetId": "string",
  "material": "string",
  "placement": "string",
  "note": "string",
  "sortOrder": 0,
  "collectionId": "string"
}

Responses

StatusMeaning
201Registered
400ASSET_REQUIRED
401Unauthorized
403role_cannot_edit_marks
404No such asset or collection in this house
409ALREADY_REGISTERED at this level

201 returns:

{
  "mark": {
    "id": "string",
    "assetId": "string",
    "collectionId": "string",
    "material": "string",
    "placement": "string",
    "note": "string",
    "sortOrder": 0,
    "filename": "string",
    "createdAt": "2026-07-27T09:00:00.000Z"
  }
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/brand/marks" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"assetId":"string","material":"string","placement":"string","note":"string","sortOrder":0,"collectionId":"string"}'

DELETE /api/toolkit/brand/marks/{id}

Take a rendition out of the rotation

THE IMAGE SURVIVES. The bytes are hers, in her library, and un-registering a rendition is not a request to delete a picture she uploaded — deleting the asset itself is the library's own door.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Responses

StatusMeaning
200Out of the rotation; the asset is untouched
401Unauthorized
403role_cannot_edit_marks
404No such rendition in this house

200 returns:

{
  "ok": true,
  "id": "string"
}

Call it

curl -X DELETE "https://api.flam.fashion/api/toolkit/brand/marks/{id}" \
  -H "Authorization: Bearer $FLAM_API_KEY"

PATCH /api/toolkit/brand/marks/{id}

Correct a rendition's material, placement, note or riding order

Partial — an absent key keeps what is stored. material cannot be cleared; placement and note can be, by sending an empty string or null. Nothing is charged.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Request bodyapplication/json (required)

FieldTypeRequiredNotes
materialstringno
placementstring | nullno
notestring | nullno
sortOrderintegerno
{
  "material": "string",
  "placement": "string",
  "note": "string",
  "sortOrder": 0
}

Responses

StatusMeaning
200Corrected
400BAD_MATERIAL
401Unauthorized
403role_cannot_edit_marks
404No such rendition in this house

200 returns:

{
  "mark": {
    "id": "string",
    "assetId": "string",
    "collectionId": "string",
    "material": "string",
    "placement": "string",
    "note": "string",
    "sortOrder": 0,
    "filename": "string",
    "createdAt": "2026-07-27T09:00:00.000Z"
  }
}

Call it

curl -X PATCH "https://api.flam.fashion/api/toolkit/brand/marks/{id}" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"material":"string","placement":"string","note":"string","sortOrder":0}'