ai
Covers 8 API operations in the ai category.
| Method | Path | Description |
|---|---|---|
| GET | /v1/ai/models | AI model catalog (runlot ai models) |
| GET | /v1/orgs/{orgSlug}/projects/{projectName}/ai | AI status (runlot ai usage) |
| POST | /v1/orgs/{orgSlug}/projects/{projectName}/ai | Turns on AI (a deployment calls this after reading "ai": true) |
| DELETE | /v1/orgs/{orgSlug}/projects/{projectName}/ai | Turns off AI (runlot ai delete) |
| GET | /v1/orgs/{orgSlug}/ai/credit | AI credit balance and auto top-up settings (runlot ai credit) |
| GET | /v1/orgs/{orgSlug}/ai/ledger | AI ledger |
| POST | /v1/orgs/{orgSlug}/ai/credit/checkout | Credit top-up checkout |
| PUT | /v1/orgs/{orgSlug}/ai/autotopup | Turns auto top-up on or off |
GET /v1/ai/models
Every model env.ai can call (docs/ai.md §2.3). Any signed-in user can read it — what you can
buy is not an org secret, and this list is the price list.
There are two lanes. frontier passes the provider's list price straight through
(listPrice: true, zero margin) — that is why the provider's name stays in the model id and
provider is included. value models are the ones we buy wholesale and sell at our own price, so
provider is not included: that is a value we swap whenever we want.
Prices are integer microcents per million tokens (1 µ¢ = 1e-8 USD). The point is that the server does not fix the number of decimal places — a small call rounds to 0 in cents (docs/ai.md §3.1), and that fact must not be tied to a single format string.
A model id that is not on this list returns 400 at call time. There is no arbitrary model passthrough.
operationId listAIModels
| Status code | Description | Response body |
|---|---|---|
| 200 | Catalog | object |
| 401 | — | — |
GET /v1/orgs/{orgSlug}/projects/{projectName}/ai
Whether AI is turned on, the four limits, the org's balance, and the last 24 hours of usage
(docs/ai.md §6). The limits come populated even when AI is off — they are the material for "here is
what the limits would be if you turned this on", and granted is the real state.
0 in maxOutputTokens and maxInputTokens means the model decides.
spentMicroCents24h cannot be built from usage_hourly, so it is read from the ledger instead —
prices are a function of the moment, which is why 0046 split the two tables.
viewer or above.
operationId getAI
| Status code | Description | Response body |
|---|---|---|
| 200 | AI status | AIStatus |
| 403 | — | — |
| 404 | — | — |
| 503 | no_core | Error |
POST /v1/orgs/{orgSlug}/projects/{projectName}/ai
This is idempotent. There is no request body — neither the limits nor the model list is a value the user sets.
Once it is on, the node connects env.ai on the next convergence. The bundle must also say
\\"ai\\": true — turning it on is the CP's side, and which system workers start is the bundle's
side.
There is no node credential check: the upstream credentials (Bedrock, Vertex) live in the node's site.env, so the CP has no record of them. On a node without them, turning it on still succeeds and the call returns 503 (an open item in docs/ai.md §9).
member and above. Audited as ai.grant.
operationId grantAI
| Status code | Description | Response body |
|---|---|---|
| 200 | Status after turning it on (the existing value if it was already on) | AIStatus |
| 403 | — | — |
| 404 | — | — |
| 503 | no_core | Error |
DELETE /v1/orgs/{orgSlug}/projects/{projectName}/ai
From the next deployment on, there is no env.ai. This is idempotent.
The ledger is not deleted — it is the record of money already paid. The credit balance stays as well: the balance belongs to the org, and what you are turning off belongs to the project.
admin. Audited as ai.revoke.
operationId revokeAI
| Status code | Description | Response body |
|---|---|---|
| 204 | turned off | — |
| 403 | — | — |
| 404 | — | — |
| 503 | no_core | Error |
GET /v1/orgs/{orgSlug}/ai/credit
The balance is in microcents and can go negative (docs/ai.md §3.3 — the balance check runs before a call and the deduction after it, so a single call that has already started can take the balance past zero).
If hasPaymentMethod is false, auto top-up cannot be turned on — there is no saved card, and
the first top-up is always manual (that is what saves the card).
autoTopupsToday and autoTopupPerDay come together. That cap is the ceiling on a day's spend,
so a screen has to show both to answer "why didn't it top up".
member or above.
operationId getAICredit
| Status code | Description | Response body |
|---|---|---|
| 200 | Credit status | AICredit |
| 403 | — | — |
GET /v1/orgs/{orgSlug}/ai/ledger
This is the source of truth for amounts (0046). kind is call (negative), topup (positive),
or adjust. The cursor is ledger_id — cutting by time would split two rows in the same
millisecond across a page boundary.
member or above.
operationId listAILedger
| Parameter | Location | Required | Type | Description |
|---|---|---|---|---|
limit | query | No | integer | — |
before | query | No | integer | rows before this ledger_id |
| Status code | Description | Response body |
|---|---|---|
| 200 | Ledger | object |
| 403 | — | — |
POST /v1/orgs/{orgSlug}/ai/credit/checkout
Returns an embedded checkout URL (the same shape as docs/billing.md §3.1 — the dashboard opens it in an iframe).
A browser coming back is not proof. The one thing that raises the balance is the order.paid
webhook, and the screen polls the balance after the success event.
microCents must be one of the listed tiers — auto top-up uses the same list (it charges with
nobody watching, so the amount has to be one of the enumerated values), and leaving manual top-up
free-form would create two tables.
operationId createAICreditCheckout
Request body: application/json · object
| Status code | Description | Response body |
|---|---|---|
| 200 | Checkout | object |
| 400 | The top-up amount is not one of the offered tiers, or returnTo is not an allowed address | Error |
| 403 | — | — |
| 501 | billing_off — this installation has no credit product configured | Error |
PUT /v1/orgs/{orgSlug}/ai/autotopup
Turning it on is explicit consent (docs/ai.md §3.5). The screen shows "when the balance falls below X, we charge Y to the saved card, at most N times a day" verbatim and takes a checkbox — card network rules require prior consent for off-session charges, and that sentence is the content of the consent.
The server checks three things: whether a payment method is saved (409 if not), whether the amount is on the tier list, and whether the threshold is greater than 0 and smaller than the top-up amount. A threshold of 0 makes the whole asynchronous top-up window return 402, and a threshold at or above the top-up amount leaves the balance still below the threshold after a top-up, so it keeps topping up to the daily cap — that is a trap, not a setting.
admin. Audited as ai.autotopup.on / ai.autotopup.off.
operationId setAIAutoTopup
Request body: application/json · object
| Status code | Description | Response body |
|---|---|---|
| 200 | Turned on | object |
| 204 | turned off | — |
| 400 | The amount is not on the tier list, the threshold is 0 or less, or the threshold is at or above the top-up amount | Error |
| 403 | — | — |
| 409 | no_payment_method — there is no saved card. Top up manually once first | Error |