Account, usage & keys
Everything about the account behind a key: what it can spend, what it has spent, and which keys can act as it.
GET /v1/account
The current balance, plan, and subscription state. The endpoint to call before spending, and the cheapest way to verify a key works.
Returns
| Field | Type | Description |
|---|---|---|
remainingCredits | number | max(0, monthlyBudgetCredits + purchasedCredits − spentCreditsThisPeriod). What you can actually spend. |
user.email · user.name · user.image | Identity of the account. | |
user.tier | enum | none (Free) · standard · pro · max. |
user.currentPeriodEnd | string | null | ISO date the billing period ends and the monthly grant resets. |
user.cancelAtPeriodEnd | boolean | Subscription is set to lapse at period end. |
user.pastDue | boolean | A renewal charge failed and is being retried. The tier is still active — a grace period, not a lockout. |
user.spentCreditsThisPeriod | number | Credits consumed since the period started. |
user.purchasedCredits | number | Top-up balance. Never expires and isn't reset by the monthly cycle. |
plan.tier · plan.monthlyPriceUsd · plan.monthlyBudgetCredits | The account's current plan. |
curl -s https://api.corte.so/v1/account \
-H "Authorization: Bearer corte_sk_…"
{
"user": {
"email": "you@example.com",
"name": "You",
"image": null,
"tier": "pro",
"currentPeriodEnd": "2026-08-14T00:00:00.000Z",
"cancelAtPeriodEnd": false,
"pastDue": false,
"spentCreditsThisPeriod": 1288,
"purchasedCredits": 500
},
"plan": {
"tier": "pro",
"monthlyPriceUsd": 30,
"monthlyBudgetCredits": 3500
},
"remainingCredits": 2712
}
:::tip Preflight expensive work
Compare remainingCredits against the estimatedCredits a creation call would
return. It won't prevent a 402 from a concurrent spend, but it stops a batch job
from discovering the problem halfway through.
:::
GET /v1/plans
The purchasable plan ladder, cheapest first. No authentication required — this is public catalog data for pricing pages.
none (Free) is excluded; this endpoint lists what you can buy.
Returns { "plans": [...] }.
| Field | Type | Description |
|---|---|---|
tier | enum | standard · pro · max. |
monthlyPriceUsd | number | List price. |
monthlyBudgetCredits | number | Credits granted each period. |
discountedMonthlyPriceUsd | number | Present only when a promotional price is active. |
curl -s https://api.corte.so/v1/plans
{
"plans": [
{ "tier": "standard", "monthlyPriceUsd": 10, "monthlyBudgetCredits": 1000 },
{ "tier": "pro", "monthlyPriceUsd": 30, "monthlyBudgetCredits": 3500 },
{ "tier": "max", "monthlyPriceUsd": 80, "monthlyBudgetCredits": 10000 }
]
}
Any purchase — a top-up or a plan — unlocks the full model catalog; firstTopupBonusCredits
is the bonus a first top-up would add, or null once the account has bought
anything. Credits and storage are what
scale up the ladder — see Plans & pricing.
GET /v1/usage
The account's credit ledger, newest first. Every grant and every charge.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
limit | number | 100 | Max 500. |
offset | number | 0 |
Returns
| Field | Type | Description |
|---|---|---|
entries | object[] | The page. |
remainingCredits | number | Current balance, same as GET /v1/account. |
total | number | Total ledger rows for the account — page with this. |
The ledger entry
| Field | Type | Description |
|---|---|---|
id | string | Entry id. |
kind | enum | monthly_grant · topup · generation · transcription · agent_usage · storage · plugin_purchase · plugin_payout · adjustment. |
deltaCredits | number | Negative is spend, positive is a grant. |
ref | string | null | The id of what caused it — a generation job id, a Stripe reference. |
note | string | null | Human context, usually the model id. |
createdAt | string | ISO 8601. |
projectId | string | null | Set when the spend was project-billed. |
actorUserId · actorEmail | string | null | Who triggered it, when that differs from the payer. |
previewUrl · previewKind | Thumbnail of what a generation charge bought. Signed and expiring. |
curl -s "https://api.corte.so/v1/usage?limit=3" \
-H "Authorization: Bearer corte_sk_…"
{
"entries": [
{
"id": "aa41e0c7-…",
"kind": "generation",
"deltaCredits": -85,
"ref": "5f2b9c14-8a3d-4b21-9e77-0c4a1d6f8e02",
"note": "kling-3",
"createdAt": "2026-07-28T14:04:47.019Z",
"projectId": null,
"actorUserId": null,
"actorEmail": null,
"previewUrl": "https://media.corte.so/…?X-Amz-Signature=…",
"previewKind": "video"
},
{
"id": "3b8f01d5-…",
"kind": "topup",
"deltaCredits": 500,
"ref": "cs_live_a1B2c3…",
"note": "$5.00 top-up",
"createdAt": "2026-07-24T08:12:33.905Z",
"projectId": null,
"actorUserId": null,
"actorEmail": null,
"previewUrl": null,
"previewKind": null
},
{
"id": "df70a2e9-…",
"kind": "monthly_grant",
"deltaCredits": 3500,
"ref": null,
"note": "Pro monthly grant",
"createdAt": "2026-07-14T00:00:04.331Z",
"projectId": null,
"actorUserId": null,
"actorEmail": null,
"previewUrl": null,
"previewKind": null
}
],
"remainingCredits": 2712,
"total": 418
}
For per-project spend broken down by actor, use
GET /v1/projects/:id/usage.
GET /v1/health
Liveness plus which upstream integrations this deployment has credentials for. No authentication required.
curl -s https://api.corte.so/v1/health
{
"ok": true,
"providers": { "anthropic": true, "fal": true, "stripe": true, "s3": true }
}
A false provider explains an otherwise puzzling forbidden or
provider_unavailable: fal: false means generation isn't wired up on this
deployment.
API keys
Keys are how anything outside the browser authenticates. Corte stores only a SHA-256 hash, so a secret is returned exactly once, at creation. Limit: 20 keys per account.
An existing key can mint new keys. That's convenient and worth thinking about — a leaked key can create more keys, so revoking the leaked one alone may not be enough. Audit the list after any incident.
The key object
| Field | Type | Description |
|---|---|---|
id | string | Key id — what you pass to DELETE. |
name | string | Your label. |
createdAt | string | ISO 8601. |
lastUsedAt | string | null | Updated on every authenticated request. null means never used. |
scopes | string[] | null | Areas the key may reach. null is full access. |
The secret is never in this object. It exists only in the POST response.
Scopes
scopes entries are <area>:<access>, where area is one of content,
media, integrations, generation, workflows, cuts, projects,
plugins, community, build, agents, account, and access is read
(GET requests) or rw (everything). rw implies read. See
API keys → Scopes for what each area covers.
The same vocabulary bounds what an agent may reach,
with one difference: an agent can never hold the account area, and a scoped
key cannot create an agent more powerful than itself.
There is no public or delivery scope — that is a delivery token, a different credential.
A request outside a key's scopes returns 403 forbidden, naming the scope it
needed:
{ "error": { "code": "forbidden", "message": "This API key is not scoped for POST /v1/content/types — it needs \"content:rw\"" } }
The same scopes govern the MCP server: a scoped key is offered only the tools it can call.
POST /v1/api-keys
Create a key.
Body — { "name": "…", "scopes": [...] }. Both are required; name is
non-empty and trimmed to 100 characters, scopes must name at least one area.
Returns { apiKey, secret }. secret is shown once. Store it before you
read anything else.
curl -s -X POST https://api.corte.so/v1/api-keys \
-H "Authorization: Bearer corte_sk_…" \
-H "Content-Type: application/json" \
-d '{ "name": "ci-render-bot", "scopes": ["media:rw", "workflows:rw"] }'
{
"apiKey": {
"id": "c0f81a37-4d92-4b16-8e05-71a3c9d0b284",
"name": "ci-render-bot",
"createdAt": "2026-07-28T20:15:41.008Z",
"lastUsedAt": null,
"scopes": ["media:rw", "workflows:rw"]
},
"secret": "corte_sk_9f3a1c7e5b204d86af10c92e7d3b6f41a58c07e29d4b3c6"
}
invalid_params when name is empty, the account already has 20 keys, or
scopes is missing, empty, or contains an unknown entry. There is no endpoint
to change a key's scopes — revoke it and create a new one.
GET /v1/api-keys
List your keys, newest first. Secrets are never included.
Returns { "apiKeys": [...] }.
{
"apiKeys": [
{ "id": "c0f81a37-…", "name": "ci-render-bot", "createdAt": "2026-07-28T20:15:41.008Z", "lastUsedAt": "2026-07-28T20:41:19.772Z", "scopes": ["media:rw", "workflows:rw"] },
{ "id": "5a94e0b2-…", "name": "claude-code", "createdAt": "2026-07-11T09:03:12.441Z", "lastUsedAt": "2026-07-27T18:22:05.130Z", "scopes": null }
]
}
lastUsedAt is the cheapest way to find keys worth revoking: anything null or
months stale is an integration nobody is running.
DELETE /v1/api-keys/:id
Revoke a key. Effective immediately — in-flight requests with that key start
failing unauthenticated.
Returns { "ok": true }. not_found if the id isn't one of yours.
curl -s -X DELETE https://api.corte.so/v1/api-keys/c0f81a37-… \
-H "Authorization: Bearer corte_sk_…"
Related
- API keys guide — creating keys in the app, and key hygiene
- Credits explained — what things cost
- Usage & ledger — the in-app view