API · credits
Two endpoints cover the Credit lifecycle: POST /v1/meter/consume spends a Credit atomically, and GET /v1/ledger reads the history. A Credit is one server-signed value-output; execution is unmetered, the signed output is metered.
POST /v1/meter/consume
Auth: device token. Atomic, idempotent decrement. Consumption order is fixed: window → monthly → paid → granted.
200- signed decrement result.402- insufficient credits; zero writes (the balance is untouched).503- fail-closed: signing unavailable, so no decrement happens.
Every consume carries an idempotency key. A retry with the same key replays the stored result - a duplicated consume never double-charges. That is the guarantee behind safe client retries.
GET /v1/ledger
Auth: Supabase session. The caller's own credit ledger, newest first. It is not a receipt store - signed savings receipts are device-held (zero-trace).
{
"transactions": [
{ "delta": -1, "bucket": "monthly", "reason": "receipt", "created_at": 1767225300 },
{ "delta": +500, "bucket": "granted", "reason": "credit.grant", "created_at": 1767139200 }
]
}An empty ledger is honest, not an error - a new account genuinely has no transactions yet.