FLAM

Share

9 Share routes on the FLAM API: What this house has sent; Mint a link a brand can open with no account; Close a link now.

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

What this house has sent

Newest first. answered and kept are COUNTED AT READ TIME from share_mark — there is no stored rollup anywhere in this feature, because a stored rollup drifts the first time a mark changes and then the board lies, which is worse than no board.

A revoked or expired link still appears here WITH ITS MARKS. Expiry closes a door; it does not retract an opinion, and the marks are hers forever.

Responses

StatusMeaning
200Her sends
401Unauthenticated

200 returns:

{
  "shares": [
    {
      "id": "string",
      "token": "string",
      "recipientLabel": "string",
      "frames": 0,
      "answered": 0,
      "kept": 0,
      "expiresAt": "string",
      "revokedAt": "string",
      "createdAt": "string"
    }
  ]
}

Call it

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

POST /api/share

Mint a link a brand can open with no account

{ assetIds, recipientLabel? } — freezes the selection into an ORDERED SNAPSHOT and returns { url }. The link is {WEB_ORIGIN}/s#<token>: the token is in the FRAGMENT, which is never sent to any server, so it appears in no access log, no API log, no Referer and no proxy.

A share is a snapshot, never a live view. Adding a frame to the lookbook afterwards does not change an open link — an approval is an approval of what the approver SAW. She wants the brand to see two more frames? She sends a second link.

recipientLabel is free text SHE types ("Maya at Ganni"), or nothing, in which case every surface says "the link holder". FLAM never emails the recipient and never cookies them.

COSTS NOTHING. No job, no tool slug, no reserve, no price-book row.

Request bodyapplication/json

FieldTypeRequiredNotes
assetIdsstring[]yes
recipientLabelstringno
emailstring (email)no
messagestringno
{
  "assetIds": [
    "string"
  ],
  "recipientLabel": "string",
  "email": "string",
  "message": "string"
}

Responses

StatusMeaning
200The link, ready to copy
400No frames, more than 60 of them, or a bad recipient address
401Unauthenticated
404None of those frames are this house's
429Too many links minted by this house in an hour

200 returns:

{
  "id": "string",
  "url": "string",
  "token": "string",
  "frames": 0,
  "emailed": "sent"
}

Call it

curl -X POST "https://api.flam.fashion/api/share" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"assetIds":["string"],"recipientLabel":"string","email":"string","message":"string"}'

POST /api/share/{id}/revoke

Close a link now

Stamps revoked_at. Enforced on EVERY request to every public door from the next one onward — never by hiding a row from a list, and never by the client.

THE HONEST SENTENCE, and the docs page says it too: revoking stops new reads; it cannot un-download what was already downloaded. The share bytes carry max-age=3600, so an hour bounds how long they survive in a browser that already has them.

The marks stay. They are hers.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Responses

StatusMeaning
200Closed
401Unauthenticated
404Not this house's link

200 returns:

{
  "revoked": true
}

Call it

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

GET /api/share/{token}

Open a send (no account, no session)

The manifest a brand's decision-maker's browser reads. No requireActor — authority is the 256-bit token, which lives in the page's URL FRAGMENT and therefore reaches no server log, no Referer and no proxy.

It answers with LABELS AND POSITIONS ONLY: no asset ids, job ids, user ids, org id, R2 keys, judge verdicts, scores, SKUs, lineage or prices. Frames are addressed by i — an index into this share's own ordered array — so there is nothing to enumerate.

Expired, revoked and never-existed are BYTE-IDENTICAL 404s, on purpose.

Parameters

InNameTypeRequiredNotes
pathtokenstringyes

Responses

StatusMeaning
200The frames she sent, in her order
404Expired, revoked, or never existed — one body for all three

200 returns:

{
  "house": "string",
  "recipientLabel": "string",
  "expiresAt": "string",
  "finishedAt": "string",
  "frames": [
    {
      "i": 0,
      "name": "string",
      "note": "string",
      "w": 0,
      "h": 0,
      "mark": "keep",
      "markNote": "string",
      "drawings": null
    }
  ]
}

Call it

curl -X GET "https://api.flam.fashion/api/share/{token}" \
  -H "Authorization: Bearer $FLAM_API_KEY"

GET /api/share/{token}/f/{i}

The bytes of one frame in a send

Full quality, byte-for-byte the original R2 object — no re-encode, no watermark, no downscale. ?thumb=1 serves the small one when the house holds one and falls back to the original when it does not. ?export=1 means the recipient is TAKING the bytes, and writes the same first-party keep signal the library writes (never on a thumbnail — a wall is not a keep).

Cache-Control: private, max-age=3600 and NOT the library's year-long immutable: revocation cannot reach into a cache, so an hour bounds how long a revoked link's bytes survive in the recipient's browser. Revoking stops new reads; it cannot un-download what was already taken.

i is a position in this share's array. Past its end is a 404, like everything else here.

Parameters

InNameTypeRequiredNotes
pathtokenstringyes
pathistringyes
querythumbstringno
queryexportstringno

Responses

StatusMeaning
200The image.
404Expired, revoked, out of range, or never existed

Call it

curl -X GET "https://api.flam.fashion/api/share/{token}/f/{i}" \
  -H "Authorization: Bearer $FLAM_API_KEY"

POST /api/share/{token}/finish

Say you are done

The end of the walk. A reviewer never really signs off — they just stop — so until this existed there was no moment the house could tell the sender about. This is them SAYING so, which is a different fact from the marks.

IDEMPOTENT. Finishing twice keeps the FIRST stamp and writes no second notification, because "when did they tell me they were done" has one honest answer. It does NOT close the link: they may reopen it, change a mark and finish again.

Answering with nothing marked is refused — there is no news in it.

No account, no session, no tokens, rate-limited like the mark door.

Parameters

InNameTypeRequiredNotes
pathtokenstringyes

Responses

StatusMeaning
200Their answers are with her
400Nothing was marked, so there is nothing to send
404Expired, revoked, or never existed

200 returns:

{
  "finished": true,
  "kept": 0,
  "passed": 0
}

Call it

curl -X POST "https://api.flam.fashion/api/share/{token}/finish" \
  -H "Authorization: Bearer $FLAM_API_KEY"

POST /api/share/{token}/mark

Say keep or pass on one frame

{ i, verdict, note?, drawings? } — an UPSERT keyed on (share, frame), so a reviewer may change their mind and the last word stands. One mark per frame per LINK, which is one mark per recipient per frame: two decision-makers are two links.

THE HANDLER TAKES NO ASSET ID FROM THE WIRE. It resolves share.asset_ids[i] server-side, so a mark can only ever touch a frame inside this share — narrow by construction, not by a check.

drawings is COORDINATES: [{kind:'circle'|'free', points:[[x,y],…]}], every coordinate a finite float in [0,1], at most 3 strokes of at most 120 points. SVG, HTML, a d string, an object, NaN or a coordinate of 1.5 are each a 400 — the recipient sends numbers, and the house draws them in its own ink.

This writes ONE notification per share, on the FIRST mark. The count she reads ("12 of 14") is joined at READ time, so one row stays true as the reviewer keeps going.

Parameters

InNameTypeRequiredNotes
pathtokenstringyes

Request bodyapplication/json

FieldTypeRequiredNotes
iintegeryes
verdict"keep" | "pass"yes
notestringno
drawingsShareDrawingsno
{
  "i": 0,
  "verdict": "keep",
  "note": "string",
  "drawings": [
    {
      "kind": "circle",
      "points": [
        null
      ],
      "note": "string"
    }
  ]
}

Responses

StatusMeaning
200The mark stands
400A note over 600 characters, or a drawing that is not numbers
404Expired, revoked, out of range, or never existed
429Too many marks from one address in an hour

200 returns:

{
  "recorded": true
}

Call it

curl -X POST "https://api.flam.fashion/api/share/{token}/mark" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"i":0,"verdict":"keep","note":"string","drawings":[{"kind":"circle","points":[null],"note":"string"}]}'

GET /api/toolkit/folders/{id}/coverage

What is done in this collection, and what is still missing

DERIVED AT READ TIME. There is no status column, no stored rollup and no cached count anywhere in this feature — a stored rollup drifts the first time a mark, an approval or a look's cast changes, and then the board lies, which is worse than no board. The source rows ARE the state.

missing counts pieces with no director-approved frame. approved there means assets.approved_at IS NOT NULL — HER decision, never a recipient's keep. A recipient's enthusiasm is not completion.

Per piece, four states: covered (≥1 approved frame, or the piece's own packshot approved), changes-asked (an open pass came back and nothing of this piece is approved — an unresolved disagreement is unfinished work), awaiting (in a live share, nothing back yet), not-sent.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Responses

StatusMeaning
200The collection's coverage
401Unauthenticated
404Not this house's collection

200 returns:

{
  "pieces": 0,
  "covered": 0,
  "missing": 0,
  "states": {}
}

Call it

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

GET /api/toolkit/share-marks

What came back, on these frames

?assetIds=a,b,c — the recipients' marks on the named frames, for her library tiles and her lightbox. Org-scoped; a frame she does not own returns nothing rather than an error.

WHEN TWO RECIPIENTS DISAGREE, BOTH ROWS COME BACK AND NO RULE PICKS A WINNER. Maya keeping frame 7 while Tom passes it is not a data problem — it is the most important thing on the screen, and a house that silently computes a winner has hidden the one fact she needed. The tie is broken by her, in the decision layer.

Parameters

InNameTypeRequiredNotes
queryassetIdsstringyes

Responses

StatusMeaning
200Evidence, newest first
401Unauthenticated

200 returns:

{
  "marks": [
    {
      "assetId": "string",
      "recipientLabel": "string",
      "verdict": "keep",
      "note": "string",
      "drawings": [
        null
      ],
      "at": "string"
    }
  ]
}

Call it

curl -X GET "https://api.flam.fashion/api/toolkit/share-marks?assetIds=<assetIds>" \
  -H "Authorization: Bearer $FLAM_API_KEY"