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
| Status | Meaning |
|---|---|
200 | The profile |
401 | Unauthorized |
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 body — application/json (required)
| Field | Type | Required | Notes |
|---|---|---|---|
visionEn | string | no | — |
vibe | string[] | no | — |
palette | string[] | no | — |
signatureKeywords | object[] | no | — |
injectEnabled | boolean | no | — |
{
"visionEn": "string",
"vibe": [
"string"
],
"palette": [
"string"
],
"signatureKeywords": [
{
"category": "string",
"phrase": "string"
}
],
"injectEnabled": true
}Responses
| Status | Meaning |
|---|---|
200 | The updated profile |
400 | BAD_JSON |
401 | Unauthorized |
403 | A 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 body — multipart/form-data (required)
| Field | Type | Required | Notes |
|---|---|---|---|
image | file[] | yes | — |
vibe | string | no | JSON string[] of vibe hints |
products | string | no | what the house sells |
Responses
| Status | Meaning |
|---|---|
200 | The extracted (and stored) DNA |
400 | NO_IMAGES / BAD_FORM |
401 | Unauthorized |
402 | INSUFFICIENT_TOKENS / AI_NEEDS_BILLING |
403 | A viewer may not spend the house's tokens |
413 | IMAGE_TOO_LARGE / SET_TOO_LARGE (32MB across the set) |
502 | AI_BAD_OUTPUT / AI_FAILED |
503 | AI_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
| In | Name | Type | Required | Notes |
|---|---|---|---|---|
| query | collectionId | string | no | A collection (asset folder) id. Unknown or another house's is 404. |
Responses
| Status | Meaning |
|---|---|
200 | The house's renditions, the collection's override, and which rides |
401 | Unauthorized |
404 | No 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 body — application/json (required)
| Field | Type | Required | Notes |
|---|---|---|---|
assetId | string | yes | An asset owned by THIS house. Re-read under the actor's org. |
material | string | yes | What it is on — "embossed on black calf". Rides the prompt verbatim. |
placement | string | no | Where on the piece — "left chest". Rides the sentence; not yet verified by the judge. |
note | string | no | — |
sortOrder | integer | no | Which renditions ride. At most two travel with a frame, lowest first. |
collectionId | string | no | Omit for the house's set. Set to override that collection whole. |
{
"assetId": "string",
"material": "string",
"placement": "string",
"note": "string",
"sortOrder": 0,
"collectionId": "string"
}Responses
| Status | Meaning |
|---|---|
201 | Registered |
400 | ASSET_REQUIRED |
401 | Unauthorized |
403 | role_cannot_edit_marks |
404 | No such asset or collection in this house |
409 | ALREADY_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
| In | Name | Type | Required | Notes |
|---|---|---|---|---|
| path | id | string | yes | — |
Responses
| Status | Meaning |
|---|---|
200 | Out of the rotation; the asset is untouched |
401 | Unauthorized |
403 | role_cannot_edit_marks |
404 | No 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
| In | Name | Type | Required | Notes |
|---|---|---|---|---|
| path | id | string | yes | — |
Request body — application/json (required)
| Field | Type | Required | Notes |
|---|---|---|---|
material | string | no | — |
placement | string | null | no | — |
note | string | null | no | — |
sortOrder | integer | no | — |
{
"material": "string",
"placement": "string",
"note": "string",
"sortOrder": 0
}Responses
| Status | Meaning |
|---|---|
200 | Corrected |
400 | BAD_MATERIAL |
401 | Unauthorized |
403 | role_cannot_edit_marks |
404 | No 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}'