FLAM

The API

The house is callable from outside the studio — an HTTP API described in OpenAPI, an organization key your own systems hold, and an MCP server so an agent can commission work directly. Outbound webhooks are designed and not yet built.

One thing here does not answer yet

Everything under what is live is in the product today and callable now. Outbound webhooks are a settled design and nothing more. They are written down because the shape will not move much, not because you can subscribe to them. If you are costing an integration this week, cost everything above that section.

A develop you start from your own system is the same develop a director starts in the studio. Same routes, same ledger, same frames. An agent gets the same house a director gets, through a key of its own.

What is live

The described surface

RouteWhat it gives you
GET /openapi.jsonThe whole surface, as OpenAPI 3.1
GET /docsThe same document, browsable

One URL describes every route, which is the door to use if you are an agent rather than a person. Most of it comes from the routes themselves. The multipart and socket routes are contracted by hand and merged into the same document, because serving only the automatic half would show a third of the API and look complete.

Routes also carry a runnable request in the Bruno collection in the repo, and CI refuses any route added from here on without one. The surface predates that rule, so a recorded list of older routes is still catching up, and it may only shrink. A route nobody can call is a promise nobody can keep.

Signing in

Two doors, and they arrive at the same place. A browser session, or an organization key on the Authorization header. Both resolve to the same house, the same role and the same 403s, so a route behaves identically whether a person or an agent asked.

Organization keys

A key belongs to the house rather than to a person, so taking a seat off the team cannot orphan a running integration.

RouteWhat it does
POST /api/keysMint one. The raw key is returned here and nowhere else
GET /api/keysEvery key the house has minted, revoked ones included
DELETE /api/keys/:idRevoke, effective on the very next request

Send it as Authorization: Bearer flam_sk_…. Only the hash is stored, so a key that is lost is re-minted, never recovered. Revoking is a row update read on every call, so there is no cache to wait out.

A key carries one of two roles. viewer reads and spends nothing. member may spend the house's tokens. Neither reaches money: no key starts a subscription, buys a top-up or a model, or invites a seat, and no key can mint or revoke another key. The worst a leaked key does is spend tokens you had already bought, so revoke it and carry on.

An agent's own door

POST /api/mcp is an MCP server: JSON-RPC over one HTTP request, authed by the key above. It is not a wrapper around the routes below. It is the six things an agent needs to commission work, in shapes an agent can hold — an asset id where a person would upload a file, one call where a person would make four.

ToolWhat it does
list_capabilitiesWhat the house can develop, and what each costs
check_balanceSpendable tokens, and what is on hold
list_assetsThe house's frames, filterable
start_developCommission a develop. Needs a member key
get_developOne develop's state and result
list_developsRecent develops, newest first

It answers with a single JSON object rather than a stream, so an agent starts a develop and then watches it with get_develop, or over the socket below. There are no MCP resources, no prompts and no server-initiated sampling.

Submitting a develop

Long work does not block. Submit, take the id, listen.

POST /api/toolkit/jobs/submit accepts a multipart form and answers 202 with { jobId, status: "queued" }.

FieldWhat it does
toolWhich develop to run. An unrecognised one comes back UNKNOWN_TOOL.
imagesAt least one, 12 MB each at most.
quality1k, 2k or 4k. This is what the frame costs. See quality and cost.
idempotencyKeyYours to choose. Send the same key twice and you get the job you already have, not a second charge.
runIdOptional. Frames sharing one group together as a single run in the Darkroom.

Tokens are held before anything is queued. If the balance will not cover the work you get 402 with need and spendable, and nothing is created or charged. The hold settles when the frame lands and is released when it fails, so an interrupted develop costs nothing.

Errors are named rather than narrated: NO_IMAGE, IMAGE_TOO_LARGE, BAD_FORM, INSUFFICIENT_TOKENS, ENQUEUE_FAILED. Switch on the string.

Listening for the result

GET /api/toolkit/jobs/ws upgrades to a WebSocket carrying every state change on your organization's develops. It is addressed per organization rather than per person, so a run one seat started is watchable from another. An idle client makes no requests and still knows the moment a frame lands.

If your client was away, GET /api/toolkit/jobs?since= replays what it missed. POST /api/toolkit/jobs/:id/cancel holds a run; /duplicate runs it again.

How a purchase becomes yours

Worth knowing before you build anything on top of a checkout. A browser returning from payment grants nothing. The payment provider's signed delivery is the only thing that writes an entitlement, verified and deduplicated on arrival. Read the entitlement, never the redirect.

Outbound webhooks

One thing here is designed and not written, and this is it. Nothing dispatches today. It is described because the shape is settled and you may want to plan around it.

Signed and retried, so your system learns a develop has settled without asking. The events:

EventWhen
run.startedA run was accepted and has capacity
run.frame.settledOne frame finished and was charged
run.settledEvery frame in the run finished
run.failedThe run ended without completing
purchase.grantedA payment completed and something pre-made became yours

Until these exist, the socket above is the way to be told, and it is the same way the studio itself is told.

When they land, a change to a public capability becomes one change across five places at once: the route, its Bruno request, its OpenAPI description, its webhook event and its MCP tool.