FLAM

Admin

43 Admin routes on the FLAM API: Grant / revoke toolkit or tool access (admin); Token/cost usage summary from the ledger (admin); The platform analyst's.

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.

POST /api/toolkit/admin/access

Grant / revoke toolkit or tool access (admin)

Request bodyapplication/json (required)

FieldTypeRequiredNotes
action"grant_toolkit" | "revoke_toolkit" | "grant_tool" | "revoke_tool"yes
userIdstringno
emailstringno
toolkitIdstringno
toolIdstringno
{
  "action": "grant_toolkit",
  "userId": "string",
  "email": "string",
  "toolkitId": "string",
  "toolId": "string"
}

Responses

StatusMeaning
200{ ok, count }
400Missing action / item id / target
403Session email not in the admin allowlist

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/access" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action":"grant_toolkit","userId":"string","email":"string","toolkitId":"string","toolId":"string"}'

GET /api/toolkit/admin/ai-costs

Token/cost usage summary from the ledger (admin)

Responses

StatusMeaning
200Settled tokens + real USD per tool, per day, and totals
403Session email not in the admin allowlist

200 returns:

{
  "byTool": [
    {
      "tool": "string",
      "tokens": 0,
      "cost_usd": 0,
      "n": 0
    }
  ],
  "daily": [
    {
      "day": "string",
      "tokens": 0,
      "cost_usd": 0
    }
  ],
  "totalTokensSettled": 0,
  "totalCostUsd": 0
}

Call it

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

GET /api/toolkit/admin/analyst

The platform analyst's latest 12-hour digest (admin only)

The latest thing the platform analyst wrote, plus the headlines behind it. The analyst itself runs in services/ai-worker on a 12-hour cron (0 */12 * * *, src/analyst.ts); this route only reads, and there is deliberately no POST — a digest that can be triggered from a browser moves the baseline the next delta is measured against. It is a DELTA, not a dashboard: /api/toolkit/admin/judge/rollup already prints the totals, and the product here is the sentence the rollup cannot say. thin: true means the analyst REFUSED to narrate — below the floor no model is called at all, so model is null, actions is empty and the headline states the count and the floor. movements are computed in TypeScript from two counted snapshots before any model exists in the story, each carrying both denominators, and rubricVersion plus judgeModel are part of every cell key so a rubric bump breaks the comparison instead of being reported as a quality change. Every actions entry is a PROPOSAL — nothing in FLAM applies one. Every read writes an admin_access_log row; counts and one sentence, no imagery, no prompt, no email, no house name.

Parameters

InNameTypeRequiredNotes
queryhistoryintegernoHow many previous headlines to return.

Responses

StatusMeaning
200The latest digest with its movements and proposed actions, the previous headlines, and the caveats that say what a thin digest means
403Session email not in the admin allowlist

Call it

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

PATCH /api/toolkit/admin/analyst/cadence

Set the analyst's window (admin only)

D12's actual complaint, in one route — "cannot change its window without a deploy." The window was a WINDOW_HOURS constant marked /** Not negotiable */; it is now a row in analyst_config, and the next digest carries the window_hours it was written under so an old digest still says what it was measuring. Persists. Writes an admin_access_log row.

Request bodyapplication/json (required)

FieldTypeRequiredNotes
hoursintegeryesThe lookback window in hours. 168 is a week — the founder's own override of the 12-hour default.
enabledbooleannoOff means the run verb refuses. The analyst has no other trigger, so this is the whole switch.
{
  "hours": 0,
  "enabled": true
}

Responses

StatusMeaning
200The cadence as stored
400A window outside the accepted range
403Session email not in the admin allowlist

Call it

curl -X PATCH "https://api.flam.fashion/api/toolkit/admin/analyst/cadence" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"hours":0,"enabled":true}'

POST /api/toolkit/admin/analyst/run

Run the analyst now (admin only)

D12: "it is not a schedule." The 12-hour cron is gone from services/ai-worker/wrangler.toml — the analyst runs when a person presses this, and never otherwise. An analysis nobody asked for is a row nobody reads and a baseline the next delta is silently measured against. A SECOND PRESS INSIDE THE WINDOW COSTS NOTHING. The claim SQL in src/analyst.ts already refuses to start a second run inside window_hours, so this returns the digest that already exists rather than spending the model call again. That is what makes the button safe to put in front of a person. The floor still holds: below the readable-verdict minimum the digest is thin, actions is empty and no model is called at all. Writes an admin_access_log row.

Responses

StatusMeaning
200The digest — freshly written, or the existing one when a run is already inside the window
403Session email not in the admin allowlist

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/analyst/run" \
  -H "Authorization: Bearer $FLAM_API_KEY"

GET /api/toolkit/admin/develops

Develop runs across the house, newest first

Responses

StatusMeaning
200Runs
403Session email not in the admin allowlist

Call it

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

POST /api/toolkit/admin/develops/action

Retry or cancel a develop

Cancel carries the Darkroom's honest semantics — queued frames release their hold, a frame already rendering finishes and still costs.

Responses

StatusMeaning
200The action's honest tally
403Session email not in the admin allowlist

Call it

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

GET /api/toolkit/admin/emails

Every letter the house sent (email_log)

The list omits html on purpose — a hundred rendered emails is megabytes of body for a table that shows none of it. Pass ?id= for ONE row WITH its byte-exact archived HTML, exactly as the recipient received it. skipped is the dev-safe lane, not a fault.

Responses

StatusMeaning
200Letters, or one letter with its archive
403Session email not in the admin allowlist
404No such letter

Call it

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

GET /api/toolkit/admin/entitlements

Real-money purchases across the house

Responses

StatusMeaning
200Entitlements
403Session email not in the admin allowlist

Call it

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

POST /api/toolkit/admin/entitlements/revoke

Revoke a granted entitlement (a refund's other half)

Responses

StatusMeaning
200Revoked
403Session email not in the admin allowlist

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/entitlements/revoke" \
  -H "Authorization: Bearer $FLAM_API_KEY"

GET /api/toolkit/admin/failures

Failures grouped by what the provider actually said

Twelve rows of "the provider returned no frame" is one problem wearing twelve uuids, so failures group on the error text with ids and digits knocked out. Each group carries how many HOUSES it reached — one house is probably their input, several is ours — and the whole real message from the most recent occurrence. Refused emails ride along.

Responses

StatusMeaning
200Grouped failures, plus refused emails
403Session email not in the admin allowlist

Call it

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

GET /api/toolkit/admin/frames

Every house's outputs, browsable

What is actually being MADE on FLAM. Narrows server-side by house (organization id), tool (source_tool), age in days, and origin — made is anything the house developed, brought is what a director uploaded. The filter lists ride along in the same answer so the panel never needs a second round trip.

Responses

StatusMeaning
200Frames, plus the houses and tools that exist
400Invalid input
403Session email not in the admin allowlist

Call it

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

GET /api/toolkit/admin/frames/{id}/file

The bytes of any house's frame (admin only)

assets.ts's streamAsset owner-gates on organization_id and answers 404 to everyone else — correct for a director, and fatal for an admin who has to look at the frame a customer is complaining about. Widening that route would put "or an admin" inside the DIRECTOR path, where a bug in the allowlist becomes a cross-house read for real users. This one is admin-only from its first line.

Responses

StatusMeaning
200The image bytes
403Session email not in the admin allowlist
404No such frame

Call it

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

POST /api/toolkit/admin/grant-tokens

Comp a token grant to a user (admin)

Request bodyapplication/json (required)

FieldTypeRequiredNotes
userIdstringyes
tokensintegeryes
expiryDaysinteger | nullno
reasonstringno
{
  "userId": "string",
  "tokens": 0,
  "expiryDays": 0,
  "reason": "string"
}

Responses

StatusMeaning
200{ ok, granted, tokens, expiresAt }
400USER_REQUIRED | BAD_TOKENS (body includes max)
403Session email not in the admin allowlist
404USER_NOT_FOUND

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/grant-tokens" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"userId":"string","tokens":0,"expiryDays":0,"reason":"string"}'

POST /api/toolkit/admin/invite

Open the door for one email (admin)

Issues the PLATFORM-gate invitation: an invitations row plus permanent email-keyed studio access, so the invitee lands usable on signup. Idempotent — re-issuing an open door returns the same token and opens nothing twice. This is NOT the seat invitation (/api/toolkit/org/seats/invite).

Request bodyapplication/json

FieldTypeRequiredNotes
emailstring (email)yes
{
  "email": "noa@atelierberger.com"
}

Responses

StatusMeaning
200The door is open for this address.
400Bad address
403Not an admin

200 returns:

{
  "email": "string",
  "token": "string",
  "url": "string",
  "status": "issued",
  "toolkitsOpened": 0
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/invite" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"noa@atelierberger.com"}'

GET /api/toolkit/admin/invite-codes

List invitation codes (admin)

Newest first, live and dead together. usedCount is doors opened; joinedCount is how many of those addresses became an account — the conversion of one channel.

Responses

StatusMeaning
200Every code the house has minted.
403Not an admin

200 returns:

{
  "codes": [
    {
      "id": "string",
      "code": "FLAM-INNER-7QK4M2",
      "label": "string",
      "grantTokens": 0,
      "maxUses": 0,
      "usedCount": 0,
      "joinedCount": 0,
      "expiresAt": "string",
      "revokedAt": "string",
      "createdAt": "string"
    }
  ]
}

Call it

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

POST /api/toolkit/admin/invite-codes

Mint a shareable invitation code (admin)

Mints LABEL-XXXXXX — a vanity prefix plus six characters of Crockford base32. Redeeming it writes an ordinary invitation for the redeemer's address, so the invite-only gate is unchanged. maxUses is mandatory: a code that travels without a cap is an uncapped spend.

Request bodyapplication/json

FieldTypeRequiredNotes
labelstringyes
maxUsesintegeryes
grantTokensintegerno
expiresInDaysintegerno
{
  "label": "FLAM_INNER",
  "maxUses": 25,
  "grantTokens": 2000,
  "expiresInDays": 30
}

Responses

StatusMeaning
200The code, in full. Copy it now or read it back from the list later.
400Missing a label, a cap, or a sane number.
403Not an admin
409The minted code collided. Ask again.

200 returns:

{
  "id": "string",
  "code": "FLAM-INNER-7QK4M2",
  "label": "string",
  "grantTokens": 0,
  "maxUses": 0,
  "usedCount": 0,
  "joinedCount": 0,
  "expiresAt": "string",
  "revokedAt": "string",
  "createdAt": "string"
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/invite-codes" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"FLAM_INNER","maxUses":25,"grantTokens":2000,"expiresInDays":30}'

POST /api/toolkit/admin/invite-codes/revoke

Kill an invitation code (admin)

Immediate and final — the code stops redeeming on the next request. Nobody who already came through it loses anything: their invitation and their tokens are theirs. POST, not DELETE: this house registers its destructive verbs as POST (see /admin/offerings/retire, whose DELETE answered 404 for a week).

Request bodyapplication/json

FieldTypeRequiredNotes
idstringyes
{
  "id": "string"
}

Responses

StatusMeaning
200The code is dead.
400No id given
403Not an admin
404No such code

200 returns:

{
  "id": "string",
  "code": "FLAM-INNER-7QK4M2",
  "label": "string",
  "grantTokens": 0,
  "maxUses": 0,
  "usedCount": 0,
  "joinedCount": 0,
  "expiresAt": "string",
  "revokedAt": "string",
  "createdAt": "string"
}

Call it

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

GET /api/toolkit/admin/invite-requests

List invitation requests (admin)

Responses

StatusMeaning
200Every request, newest first.
403Not an admin

200 returns:

{
  "requests": [
    {
      "id": "string",
      "name": "string",
      "house": "string",
      "email": "string",
      "collection": "string",
      "status": "string",
      "createdAt": "string",
      "answers": [
        {
          "question": null,
          "answer": null
        }
      ]
    }
  ]
}

Call it

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

GET /api/toolkit/admin/judge/rollup

The judge's verdicts, counted (admin only)

L1, the measurement layer — the same rows the quality browser lists, aggregated. Defect code × tool × rubric version × judge model, over time, for one house (?house=) or across the whole platform (omit it). No new table: this is a GROUP BY, and nothing is written. rubricVersion and judgeModel are grouping keys on every row including the trend, because two scores from different rubrics are not comparable and a line drawn through a rubric change is a lie — rubrics names the boundaries so the caller can break the series. Every rate ships with its denominator (frames of of). unreadable is excluded from every defect and flag rate and reported on its own: it is US failing to look, not a clean frame. outcomes joins generation_feedback so flagged can be checked against what the director then did, and caveats.deleted is NOT_YET_MEASURABLE because nothing in this API deletes an asset. Every query writes an admin_access_log row. Aggregates only — no imagery, no prompt, no email.

Parameters

InNameTypeRequiredNotes
queryhousestringnoAn organization id. Omit for the whole platform.
querytoolstringno
queryrubricintegernoPin one rubric version. Omit to see every version, grouped.
querymodelstringnoPin one judge model id.
querybucket"day" | "week" | "month"noTrend granularity.
querysinceintegernoDays back. 0 or absent is all time.
querylimitintegernoMaximum cells per block.

Responses

StatusMeaning
200Totals, defect cells with denominators, per tool, per house, the trend, the rubric boundaries and the outcome join
400Invalid input
403Session email not in the admin allowlist

Call it

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

GET /api/toolkit/admin/judge/verdicts

Every judged frame, across every house (admin only)

The back office's half of the judge — the same rows /api/toolkit/judge/verdicts serves a director, plus the four things that route hides on purpose: the judge's own note, the model id, costUsd and latencyMs. Narrows server-side by house, tool, defect code, verdict band, director response, recipe arm and age. ?flagged=true is a synonym for ?band=flagged. unreadable comes back as its own count and is never folded into flagged — a wall of unreadable is US broken (a dead model id, a 404 on the frame, a timeout), not a wall of bad frames. Every query writes an admin_access_log row.

Parameters

InNameTypeRequiredNotes
queryband"flagged" | "clean" | "unreadable" | "unjudged"noflagged · clean · unreadable · unjudged. Omit for every judged frame.
querytoolstringno
queryhousestringnoAn organization id.
querydefectstringnoOne of the twelve defect codes.
querysinceintegernoDays back. 0 or absent is all time.
querylimitintegerno

Responses

StatusMeaning
200Judged frames, the three counts, the judge's spend, and the filter lists
400Invalid input
403Session email not in the admin allowlist

Call it

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

GET /api/toolkit/admin/judge/verdicts/{jobId}

One judged frame, whole (admin only)

What was asked for (her own params, scalars only — inputs is dropped because it holds base64 data URIs), what was fed in, what came out, what the judge said including its own sentence, and what the director did. feedback is all-null and recipe is the baseline arm until generation_feedback and prompt_recipes exist. Opening a frame writes an admin_access_log row: this read puts another house's unreleased imagery on a screen.

Parameters

InNameTypeRequiredNotes
pathjobIdstringyes

Responses

StatusMeaning
200The frame, its inputs, the verdict and the response
403Session email not in the admin allowlist
404No such job

Call it

curl -X GET "https://api.flam.fashion/api/toolkit/admin/judge/verdicts/{jobId}" \
  -H "Authorization: Bearer $FLAM_API_KEY"

GET /api/toolkit/admin/kept-cost

Cost per KEPT frame, per house and per tool, over a window

Cost per generation flatters us — if she develops six and keeps one, that frame cost six renders. Money out of credit_ledger (joined on generation_jobs.hold_id, never metadata->>'tool') divided by the frames that SURVIVED. The denominator basis is spoke (kept + regenerated + deleted), never runs: a frame nobody has downloaded has not spoken, and silence is not rejection. Below minKept kept frames every rate on the row is null and why says so; below minCoverage the MEASURED rate is null while credit_ledger.real_cost_usd is still mostly zero, and only the MODELLED rate answers. deletedWired is false because no delete verb exists, so "fraction thrown away" is NOT YET MEASURABLE.

Parameters

InNameTypeRequiredNotes
querysinceintegernoWindow in days
queryhousestringnoOne organization_id
querytoolstringnoOne tool slug

Responses

StatusMeaning
200Cost per kept frame with its denominator, or a stated refusal
403Session email not in the admin allowlist

Call it

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

GET /api/toolkit/admin/longstory/autonomy

Read Longstory's delegated authority

Admin-only, audited view of the active Progressive Autonomy policy and its append-only history. No saved policy means the safe recommend-only default.

Parameters

InNameTypeRequiredNotes
queryorganizationIdstringno

Responses

StatusMeaning
200The effective Longstory autonomy boundary.
403Not a platform admin

200 returns:

{
  "source": "default",
  "revision": 0,
  "selection": {
    "scopeType": "platform",
    "organizationId": null,
    "level": "observe",
    "maximumAutomaticRisk": "low",
    "maximumCanaryPercentage": 0,
    "maximumProgressivePercentage": 0,
    "maximumAutomaticChangesPerDay": 0,
    "maximumAutomaticCostUsdPerDay": 0,
    "minimumReplayPassRate": 0,
    "minimumDistinctSubjects": 0,
    "minimumDistinctOrganizations": 0
  },
  "policy": {},
  "updatedAt": "string",
  "history": [
    {
      "id": "string",
      "revision": 0,
      "status": "active",
      "level": "observe",
      "createdAt": "string",
      "supersededAt": "string"
    }
  ]
}

Call it

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

POST /api/toolkit/admin/longstory/autonomy

Create a new Longstory authority revision

Supersedes the current policy atomically. Frozen surfaces and evidence requirements cannot be weakened through this FLAM control plane.

Request bodyapplication/json (required)

FieldTypeRequiredNotes
scopeType"platform" | "organization"no
organizationIdstring | nullno
level"observe" | "recommend" | "shadow" | "experiment" | "progressive" | "autonomous"yes
maximumAutomaticRisk"low" | "medium"yes
maximumCanaryPercentagenumberyes
maximumProgressivePercentagenumberyes
maximumAutomaticChangesPerDayintegeryes
maximumAutomaticCostUsdPerDaynumberyes
minimumReplayPassRatenumberyes
minimumDistinctSubjectsintegeryes
minimumDistinctOrganizationsintegeryes
{
  "scopeType": "platform",
  "organizationId": null,
  "level": "observe",
  "maximumAutomaticRisk": "low",
  "maximumCanaryPercentage": 0,
  "maximumProgressivePercentage": 0,
  "maximumAutomaticChangesPerDay": 0,
  "maximumAutomaticCostUsdPerDay": 0,
  "minimumReplayPassRate": 0,
  "minimumDistinctSubjects": 0,
  "minimumDistinctOrganizations": 0
}

Responses

StatusMeaning
200The new active policy revision.
400Invalid policy selection
403Not a platform admin

200 returns:

{
  "source": "default",
  "revision": 0,
  "selection": {
    "scopeType": "platform",
    "organizationId": null,
    "level": "observe",
    "maximumAutomaticRisk": "low",
    "maximumCanaryPercentage": 0,
    "maximumProgressivePercentage": 0,
    "maximumAutomaticChangesPerDay": 0,
    "maximumAutomaticCostUsdPerDay": 0,
    "minimumReplayPassRate": 0,
    "minimumDistinctSubjects": 0,
    "minimumDistinctOrganizations": 0
  },
  "policy": {},
  "updatedAt": "string"
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/longstory/autonomy" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scopeType":"platform","organizationId":null,"level":"observe","maximumAutomaticRisk":"low","maximumCanaryPercentage":0,"maximumProgressivePercentage":0,"maximumAutomaticChangesPerDay":0,"maximumAutomaticCostUsdPerDay":0,"minimumReplayPassRate":0,"minimumDistinctSubjects":0,"minimumDistinctOrganizations":0}'

GET /api/toolkit/admin/memory

Observe living memory across every house

Admin-only, audited observatory for typed memories, Core projection state, outbox delivery, digestion, and retrieval. Statements and evidence ids are sensitive cross-tenant data, so every read is allowlist-gated and logged.

Responses

StatusMeaning
200The memory system's current state.
401Signed out
403Not a platform admin

200 returns:

{
  "totals": {
    "memories": 0,
    "active": 0,
    "coreProjected": 0,
    "pendingOutbox": 0,
    "failedOutbox": 0,
    "digestCostUsd": 0,
    "retrievals24h": 0,
    "injected24h": 0,
    "digests24h": 0,
    "failedDigests24h": 0,
    "averageContextMemories24h": 0,
    "latestDigestAt": "string",
    "latestDigestModel": "string",
    "populationCandidates": 0,
    "observations24h": 0,
    "observationCostUsd24h": 0
  },
  "digests": [
    {
      "id": "string",
      "organizationName": "string",
      "status": "string",
      "model": "string",
      "candidateCount": 0,
      "contextMemoryCount": 0,
      "inputTokens": 0,
      "outputTokens": 0,
      "costUsd": 0,
      "error": "string",
      "finishedAt": "string",
      "createdAt": "string"
    }
  ],
  "memories": [
    {
      "id": "string",
      "organizationId": "string",
      "organizationName": "string",
      "subjectType": "house",
      "memoryClass": "fact",
      "kind": "told",
      "statement": "string",
      "confidence": 0,
      "status": "string",
      "evidence": [
        "string"
      ],
      "coreMemoryId": "string",
      "updatedAt": "string",
      "replayEvidence": [
        {
          "id": "string",
          "startMs": 0,
          "focusMs": 0,
          "endMs": 0,
          "reason": "string",
          "archiveState": "pending",
          "providerAvailableUntil": "string",
          "openUrl": "string",
          "renders": [
            null
          ]
        }
      ]
    }
  ],
  "reflectionFindings": [
    {
      "id": "string",
      "reflectionRunId": "string",
      "kind": "population_lesson",
      "status": "proposed",
      "title": "string",
      "claim": "string",
      "recommendation": "string",
      "evidenceRefs": [
        "string"
      ],
      "replayEvidence": [
        {
          "id": "string",
          "startMs": 0,
          "focusMs": 0,
          "endMs": 0,
          "reason": "string",
          "archiveState": "pending",
          "providerAvailableUntil": "string",
          "openUrl": "string",
          "renders": [
            null
          ]
        }
      ],
      "confidence": 0,
      "risk": "low",
      "proposedReplay": {},
      "proposedTicket": {},
      "interventionId": "string",
      "reviewedAt": "string",
      "createdAt": "string"
    }
  ],
  "populationCandidates": [
    {
      "id": "string",
      "signalCode": "string",
      "status": "string",
      "observations": 0,
      "distinctOrganizations": 0,
      "confidence": 0,
      "lastObservedAt": "string",
      "expiresAt": "string"
    }
  ],
  "behaviorObservations": [
    {
      "id": "string",
      "organizationName": "string",
      "provider": "string",
      "method": "string",
      "contextVersion": "string",
      "contextDigest": "string",
      "release": {},
      "status": "string",
      "summary": "string",
      "outcome": "string",
      "confidence": 0,
      "findings": [
        {}
      ],
      "segments": [
        {}
      ],
      "costUsd": 0,
      "observedAt": "string"
    }
  ],
  "proofs": [
    {
      "id": "string",
      "lesson": "string",
      "release": {
        "name": "string",
        "version": "string",
        "exposurePercent": 0,
        "exposedSubjects": 0,
        "eligibleSubjects": 0,
        "startedAt": "string"
      },
      "conclusion": "helpful",
      "interventionStatus": "proposed",
      "metrics": [
        {
          "label": "string",
          "baseline": 0,
          "treatment": 0,
          "unit": "count",
          "betterWhen": "higher"
        }
      ],
      "evidence": {
        "strength": "weak",
        "observations": 0,
        "distinctOrganizations": 0,
        "confounders": [
          "string"
        ]
      },
      "replay": {
        "status": "not_ready",
        "passed": 0,
        "total": 0,
        "lastRunAt": "string"
      },
      "cost": {
        "measurementStatus": "complete",
        "unmeasuredComponents": [
          "string"
        ],
        "notes": [
          "string"
        ],
        "totalUsd": 0,
        "items": [
          {
            "label": null,
            "costUsd": null
          }
        ]
      },
      "updatedAt": "string"
    }
  ],
  "chatScenarios": [
    {
      "id": "string",
      "replayKey": "string",
      "version": 0,
      "title": "string",
      "risk": "low",
      "status": "pending",
      "candidateRelease": {
        "name": "string",
        "version": "string"
      },
      "passed": 0,
      "repetitions": 0,
      "passRate": 0,
      "failures": [
        {
          "repetition": 0,
          "reasons": [
            null
          ]
        }
      ],
      "durationMs": 0,
      "firstActivityMs": 0,
      "firstResponseMs": 0,
      "costUsd": 0,
      "finishedAt": "string"
    }
  ],
  "scenarioEvidence": [
    {
      "id": "string",
      "suiteId": "string",
      "suiteVersion": "string",
      "replayKey": "string",
      "replayVersion": 0,
      "title": "string",
      "layer": "deterministic_contract",
      "passed": true,
      "passRate": 0,
      "minimumPassRate": 0,
      "executions": 0,
      "totalDurationMs": 0,
      "totalCostUsd": 0,
      "userCharge": [
        {
          "unit": "string",
          "amount": 0
        }
      ],
      "agentReleases": [
        {
          "name": "string",
          "version": "string",
          "digest": "string"
        }
      ],
      "models": [
        "string"
      ],
      "featureFlags": [
        "string"
      ],
      "createdAt": "string",
      "recordedAt": "string"
    }
  ],
  "valuePlans": [
    {
      "id": "string",
      "interventionStatus": "proposed",
      "headline": "string",
      "userOutcome": "string",
      "productOutcome": "string",
      "primaryMetric": {
        "label": "string",
        "minimumEffect": 0,
        "unit": "count",
        "betterWhen": "higher"
      },
      "guardrails": [
        {
          "label": "string",
          "maximumRegression": 0,
          "unit": "count"
        }
      ],
      "evidence": {
        "minimumControl": 0,
        "minimumTreatment": 0,
        "minimumOrganizations": 0,
        "confidenceLevel": 0
      },
      "budget": {
        "maximumTotalUsd": 0,
        "maximumCostPerSuccessfulOutcomeUsd": 0
      }
    }
  ],
  "retrievalReceipts": [
    {
      "id": "string",
      "organizationName": "string",
      "mode": "shadow",
      "considered": 0,
      "selected": 0,
      "injected": 0,
      "applied": 0,
      "applicationEvidence": [
        {
          "source": "string",
          "count": 0,
          "averageConfidence": 0
        }
      ],
      "releaseName": "string",
      "releaseVersion": "string",
      "outcomeStatus": "succeeded",
      "repeatedCorrection": true,
      "error": "string",
      "createdAt": "string"
    }
  ],
  "storyGraph": {
    "schema_version": "1.0.0",
    "id": "string",
    "scope": {},
    "view": "lesson_neighborhood",
    "query": {},
    "nodes": [
      {}
    ],
    "edges": [
      {}
    ],
    "list_equivalent": [
      {}
    ],
    "layout": {},
    "truncated": true,
    "next_cursor": "string",
    "access_audit_id": "string",
    "generated_at": "string"
  }
}

Call it

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

POST /api/toolkit/admin/memory/{id}/review

Approve or reject one learned memory

Parameters

InNameTypeRequiredNotes
pathidstringyes

Request bodyapplication/json (required)

FieldTypeRequiredNotes
decision"approve" | "reject"yes
{
  "decision": "approve"
}

Responses

StatusMeaning
200The review was recorded.
403Not a platform admin
404Memory not found
409Memory was already reviewed or its predecessor changed

200 returns:

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

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/memory/{id}/review" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"decision":"approve"}'

POST /api/toolkit/admin/memory/reflection-findings/{id}/decision

Accept or reject one evidence-backed reflection proposal

Accepting creates a review-only Longstory intervention. It does not expose a change, edit instructions, or grant serving authority.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Request bodyapplication/json (required)

FieldTypeRequiredNotes
decision"accept" | "reject"yes
{
  "decision": "accept"
}

Responses

StatusMeaning
200The decision was recorded.
403Not a platform admin
404Reflection finding not found
409Reflection finding was already reviewed

200 returns:

{
  "ok": true,
  "id": "string",
  "status": "accepted",
  "interventionId": "string"
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/memory/reflection-findings/{id}/decision" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"decision":"accept"}'

GET /api/toolkit/admin/memory/replay-evidence/{id}

Open one audited session replay evidence moment

Admin-only capability-gated read. Every allowed or denied attempt is written to the immutable Longstory access ledger. Live PostHog is preferred while retained; verified R2 archives are the fallback.

Parameters

InNameTypeRequiredNotes
pathidstringyes
querymode"auto" | "archive"no

Responses

StatusMeaning
200The verified raw replay archive when the live provider has expired
302Redirect to the exact retained provider moment
403Not a platform admin
404Evidence not found
410Neither the provider nor a verified archive is available

Call it

curl -X GET "https://api.flam.fashion/api/toolkit/admin/memory/replay-evidence/{id}" \
  -H "Authorization: Bearer $FLAM_API_KEY"

POST /api/toolkit/admin/memory/replay-evidence/{id}/renders

Request an optional video rendering of replay evidence

Queues a derived MP4 or WebM from the canonical rrweb archive. Rendering is optional, audited, and inherits the evidence scope, expiry, and deletion policy.

Parameters

InNameTypeRequiredNotes
pathidstringyes

Request bodyapplication/json (required)

FieldTypeRequiredNotes
format"mp4" | "webm"no
{
  "format": "mp4"
}

Responses

StatusMeaning
200Existing or newly queued render
403Not a platform admin
404Evidence archive not found
409Canonical replay archive is not ready

200 returns:

{
  "id": "string",
  "format": "mp4",
  "state": "requested",
  "failureCode": "string",
  "requestedAt": "string",
  "completedAt": "string",
  "expiresAt": "string",
  "downloadUrl": "string"
}

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/memory/replay-evidence/{id}/renders" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"format":"mp4"}'

GET /api/toolkit/admin/memory/replay-evidence/{id}/renders/{renderId}

Download one audited replay evidence video

Parameters

InNameTypeRequiredNotes
pathidstringyes
pathrenderIdstringyes

Responses

StatusMeaning
200Verified MP4 or WebM bytes
403Not a platform admin
404Ready render not found

Call it

curl -X GET "https://api.flam.fashion/api/toolkit/admin/memory/replay-evidence/{id}/renders/{renderId}" \
  -H "Authorization: Bearer $FLAM_API_KEY"

GET /api/toolkit/admin/offerings

The catalogue, as the house sees it

Responses

StatusMeaning
200Offerings
401No valid session
403Session email not in the admin allowlist

Call it

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

POST /api/toolkit/admin/offerings

Create or update an offering (price, tier, previews)

Responses

StatusMeaning
200The saved offering
400Invalid input
403Session email not in the admin allowlist

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/offerings" \
  -H "Authorization: Bearer $FLAM_API_KEY"

POST /api/toolkit/admin/offerings/retire

Retire an offering from the board

Responses

StatusMeaning
200Retired
403Session email not in the admin allowlist

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/offerings/retire" \
  -H "Authorization: Bearer $FLAM_API_KEY"

GET /api/toolkit/admin/onboarding

Who is setting up, and how far they got (admin)

Responses

StatusMeaning
200Everyone who has started setting up, latest movement first.
403Not an admin

200 returns:

{
  "onboardings": [
    {
      "userId": "string",
      "name": "string",
      "email": "string",
      "house": "string",
      "completed": 0,
      "total": 0,
      "steps": [
        "string"
      ],
      "startedAt": "string",
      "lastStepAt": "string"
    }
  ]
}

Call it

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

GET /api/toolkit/admin/overview

The house at a glance — the first screen

Directors and houses, how many worked this week, the outstanding token float, what the engine is doing right now, what moved today against yesterday, and a fourteen-day strip. Money and tokens are separate fields and are never summed — there is deliberately no field holding the two added together.

Responses

StatusMeaning
200The house at a glance
403Session email not in the admin allowlist

Call it

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

GET /api/toolkit/admin/payments

Real transactions — subscriptions, top-ups, offerings, comps

Money reaches FLAM through three doors and the Money book only showed one. Comps are included and read as zero: hiding them makes the token float unexplainable, pricing them at anything else makes them look like revenue. amountUsd is currency and tokens is what that payment minted — separate fields, no total, because a top-up is money that bought tokens, not money and tokens.

Responses

StatusMeaning
200Transactions and standing subscriptions
403Session email not in the admin allowlist

Call it

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

GET /api/toolkit/admin/support

Tickets and feedback, with the screenshots directors sent

The one admin book that is not read out of Postgres. Tickets come from PostHog Support (the same inbox the studio's report sheet writes to) and feedback from REPORT_SURVEY's survey sent events. Every ticket's FULL first message is read per ticket rather than taken from the list, because the list truncates at ~500 characters and cuts the signed screenshot links mid-signature — a truncated signed link is a broken image, and the images are the point. screenshots are signed, expiring links on FLAM's own R2 that need no session, so they render as pictures. available: false means POSTHOG_PERSONAL_API_KEY is absent in this environment; it is a deployment fact and never an empty inbox.

Parameters

InNameTypeRequiredNotes
querylimitintegernoHow many tickets and how many feedback rows to return

Responses

StatusMeaning
200Tickets with their pictures, and every rating given
403Session email not in the admin allowlist

Call it

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

GET /api/toolkit/admin/tokenomics

What a token costs against what we charge, and the funnel

Three layers, never blended — the price book per tool AND per quality (tokens charged against credit_price_book.loaded_cost_usd), what actually ran from the jobs spine, and the conversion funnel. While costUnmeasured is true the worker still settles with a measured cost of zero, so every margin is MODELLED from the price book and the panel says so rather than printing a 100% margin.

Responses

StatusMeaning
200Unit economics, actuals and the funnel
403Session email not in the admin allowlist

Call it

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

DELETE /api/toolkit/admin/tool-items

Delete a curated item by id (admin)

Parameters

InNameTypeRequiredNotes
queryidstringyes

Responses

StatusMeaning
200{ ok, deleted }
400id required
403Session email not in the admin allowlist

Call it

curl -X DELETE "https://api.flam.fashion/api/toolkit/admin/tool-items?id=<id>" \
  -H "Authorization: Bearer $FLAM_API_KEY"

GET /api/toolkit/admin/tool-items

Curated items for one tool (admin)

Parameters

InNameTypeRequiredNotes
querytoolIdstringyes

Responses

StatusMeaning
200{ items }
400toolId required
403Session email not in the admin allowlist

Call it

curl -X GET "https://api.flam.fashion/api/toolkit/admin/tool-items?toolId=<toolId>" \
  -H "Authorization: Bearer $FLAM_API_KEY"

POST /api/toolkit/admin/tool-items

Append a curated item (admin)

Request bodyapplication/json (required)

FieldTypeRequiredNotes
toolIdstringyes
categorystringyes
contentEnstringno
titleHestringno
labelHestringno
imageUrlstringno
notesHestringno
{
  "toolId": "string",
  "category": "string",
  "contentEn": "string",
  "titleHe": "string",
  "labelHe": "string",
  "imageUrl": "string",
  "notesHe": "string"
}

Responses

StatusMeaning
200{ ok, id, sortOrder }
400toolId and category required
403Session email not in the admin allowlist

Call it

curl -X POST "https://api.flam.fashion/api/toolkit/admin/tool-items" \
  -H "Authorization: Bearer $FLAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"toolId":"string","category":"string","contentEn":"string","titleHe":"string","labelHe":"string","imageUrl":"string","notesHe":"string"}'

GET /api/toolkit/admin/users

All users with balance + access (admin)

Responses

StatusMeaning
200User list
403Session email not in the admin allowlist

200 returns:

{
  "users": [
    {}
  ]
}

Call it

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

On this page

POST /api/toolkit/admin/accessGET /api/toolkit/admin/ai-costsGET /api/toolkit/admin/analystPATCH /api/toolkit/admin/analyst/cadencePOST /api/toolkit/admin/analyst/runGET /api/toolkit/admin/developsPOST /api/toolkit/admin/develops/actionGET /api/toolkit/admin/emailsGET /api/toolkit/admin/entitlementsPOST /api/toolkit/admin/entitlements/revokeGET /api/toolkit/admin/failuresGET /api/toolkit/admin/framesGET /api/toolkit/admin/frames/{id}/filePOST /api/toolkit/admin/grant-tokensPOST /api/toolkit/admin/inviteGET /api/toolkit/admin/invite-codesPOST /api/toolkit/admin/invite-codesPOST /api/toolkit/admin/invite-codes/revokeGET /api/toolkit/admin/invite-requestsGET /api/toolkit/admin/judge/rollupGET /api/toolkit/admin/judge/verdictsGET /api/toolkit/admin/judge/verdicts/{jobId}GET /api/toolkit/admin/kept-costGET /api/toolkit/admin/longstory/autonomyPOST /api/toolkit/admin/longstory/autonomyGET /api/toolkit/admin/memoryPOST /api/toolkit/admin/memory/{id}/reviewPOST /api/toolkit/admin/memory/reflection-findings/{id}/decisionGET /api/toolkit/admin/memory/replay-evidence/{id}POST /api/toolkit/admin/memory/replay-evidence/{id}/rendersGET /api/toolkit/admin/memory/replay-evidence/{id}/renders/{renderId}GET /api/toolkit/admin/offeringsPOST /api/toolkit/admin/offeringsPOST /api/toolkit/admin/offerings/retireGET /api/toolkit/admin/onboardingGET /api/toolkit/admin/overviewGET /api/toolkit/admin/paymentsGET /api/toolkit/admin/supportGET /api/toolkit/admin/tokenomicsDELETE /api/toolkit/admin/tool-itemsGET /api/toolkit/admin/tool-itemsPOST /api/toolkit/admin/tool-itemsGET /api/toolkit/admin/users