API reference

Account and API keys

Use with your agent

Give this prompt to your AI agent. The agent does the task for you.

Read https://thirds.ai/docs/api-reference/account and help me choose the right thirds.ai endpoint and write a request. Check the fields and credentials in this reference.

Manage API keys and account identity requests, with the required credentials and response fields for each endpoint.

See all API resources or read API keys and requests.

Endpoints

requestAccountEmail

POST /v1/me/identities/email

Add an email to an account without one

Requires a signed-in browser session and its CSRF token. Sends a one-use verification link bound to that account and exact session. Open the link in the same browser while signed in, then press Complete sign in. POST /v1/auth/email/confirm proves the address and grants the current free monthly credits once. An account with an email cannot change it through this route.

Authentication: browser session cookie with a CSRF token for writes. See authentication.

ParameterLocationRequiredRule
x-csrf-tokenheaderYes{"type":"string"}

Request body: application/json, AccountEmailRequest.

StatusMeaningBody
202The request was accepted. This does not confirm mail delivery or whether another account holds the address.application/json: AccountEmailAccepted
400The JSON request is invalid or has unknown fields.application/json: ErrorEnvelope
401A live browser session is required. Expired, revoked, and suspended sessions are refused.application/json: ErrorEnvelope
403The CSRF token is missing or does not match this browser session.application/json: ErrorEnvelope
405The method is not allowed on this route.application/json: ErrorEnvelope
409This account already has an email address. The code is email_already_set.application/json: ErrorEnvelope
422The email address is invalid. The code is invalid_email.application/json: ErrorEnvelope
429The request limit was reached. Retry after the time in Retry-After.application/json: ErrorEnvelope
431The request has more than 64 headers or more than 32 KiB of header names and values.application/json: ErrorEnvelope
500The request failed internally.application/json: ErrorEnvelope
503Email delivery is not configured. The code is auth_unavailable.application/json: ErrorEnvelope

createApiKey

POST /v1/keys

Create an API key

Create one API key for the authenticated account. The full secret is returned in this response and never again; only its verifier is kept.

Authentication: bearer API key. See authentication.

Request body: application/json, NewApiKey.

StatusMeaningBody
201The key was created. This is the only response that ever carries the secret.application/json: ApiKeyWithSecret
400The name was empty, longer than 100 characters, or held a character that is not printable ASCII.application/json: ErrorEnvelope
401The request did not carry a valid, active API key.application/json: ErrorEnvelope
405The method is not allowed on this route.application/json: ErrorEnvelope
409The account already holds ten active keys. Revoke one before creating another.application/json: ErrorEnvelope
413The request body is larger than 16 KiB.application/json: ErrorEnvelope
415The request did not carry a JSON content type.application/json: ErrorEnvelope
431The request has more than 64 headers or more than 32 KiB of header names and values.application/json: ErrorEnvelope
500An internal error occurred.application/json: ErrorEnvelope

listApiKeys

GET /v1/keys

List API keys

List one bounded page of the authenticated account's keys, most recently created first. A revoked key stays in this history; its secret never appears here.

Authentication: bearer API key. See authentication.

ParameterLocationRequiredRule
limitqueryNoThe page size. Defaults to 20 and must be from 1 through 100. {"type":"integer","minimum":1,"maximum":100,"default":20}
cursorqueryNoAn opaque value from an earlier page's next_cursor. {"type":"string","maxLength":128}
StatusMeaningBody
200One page of the account's key history.application/json: ApiKeyList
400The page size or cursor is not valid.application/json: ErrorEnvelope
401The request did not carry a valid, active API key.application/json: ErrorEnvelope
405The method is not allowed on this route.application/json: ErrorEnvelope
431The request has more than 64 headers or more than 32 KiB of header names and values.application/json: ErrorEnvelope
500An internal error occurred.application/json: ErrorEnvelope

revokeApiKey

DELETE /v1/keys/{key_id}

Revoke an API key

Revoke one key of the authenticated account. Revoking a key that is already revoked returns the same answer again rather than an error, so a retried request is never rejected.

Authentication: bearer API key. See authentication.

ParameterLocationRequiredRule
key_idpathYesThe key's public identifier, such as "key_1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^key_[0-9a-f]{32}$"}
StatusMeaningBody
200The key, now revoked.application/json: ApiKey
401The request did not carry a valid, active API key.application/json: ErrorEnvelope
404No key with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, so a request can never learn which one is true.application/json: ErrorEnvelope
405The method is not allowed on this route.application/json: ErrorEnvelope
431The request has more than 64 headers or more than 32 KiB of header names and values.application/json: ErrorEnvelope
500An internal error occurred.application/json: ErrorEnvelope

updateApiKey

PATCH /v1/keys/{key_id}

Set or clear an API key's monthly overage cap

Change how many overage credits this key may take on per calendar month (UTC). A positive number sets the cap; an explicit null, or a body without the field, clears it. The cap bounds only overage: work the prepaid balance fully covers is never refused by it.

Authentication: bearer API key. See authentication.

ParameterLocationRequiredRule
key_idpathYesThe key's public identifier, such as "key_1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^key_[0-9a-f]{32}$"}

Request body: application/json, ApiKeyUpdate.

StatusMeaningBody
200The key with its new cap.application/json: ApiKey
400The cap was zero, negative, not a whole number, or the body held a field this operation does not know.application/json: ErrorEnvelope
401The request did not carry a valid, active API key.application/json: ErrorEnvelope
404No key with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, so a request can never learn which one is true.application/json: ErrorEnvelope
405The method is not allowed on this route.application/json: ErrorEnvelope
413The request body is larger than 16 KiB.application/json: ErrorEnvelope
415The request did not carry a JSON content type.application/json: ErrorEnvelope
431The request has more than 64 headers or more than 32 KiB of header names and values.application/json: ErrorEnvelope
500An internal error occurred.application/json: ErrorEnvelope

Schemas

AccountEmailRequest

FieldRequiredDescription and type
emailYes{"type":"string"}
nextNoA safe local path after proof; defaults to /account. {"type":"string"}

Schema rules: {"type":"object","additionalProperties":false}.

AccountEmailAccepted

Schema rules: {"type":"object","additionalProperties":false}.

NewApiKey

What a new key is named.

FieldRequiredDescription and type
nameYes1 to 100 printable ASCII characters. {"type":"string","minLength":1,"maxLength":100,"pattern":"^(?=.*[^ ])[ -~]+$"}

Schema rules: {"type":"object","additionalProperties":false}.

ApiKey

One key, without its secret.

FieldRequiredDescription and type
idYesThe key's public identifier: "key_" followed by 32 lowercase hexadecimal characters. {"type":"string","pattern":"^key_[0-9a-f]{32}$"}
nameYes{"type":"string"}
display_prefixYesThe first characters of the secret, enough to tell keys apart in a list. {"type":"string"}
statusYes{"type":"string","enum":["active","revoked"]}
created_atYes{"type":"string","format":"date-time"}
revoked_atYes{"type":["string","null"],"format":"date-time"}
last_used_atYes{"type":["string","null"],"format":"date-time"}
monthly_overage_capYesThe most overage credits this key may take on per calendar month (UTC). Null means the key sets no cap of its own. {"type":["integer","null"],"minimum":1}

Schema rules: {"type":"object","additionalProperties":false}.

ApiKeyUpdate

What an update may change about a key.

FieldRequiredDescription and type
nameNoNew key name. The key secret and prefix stay the same. {"type":"string","minLength":1,"maxLength":100,"pattern":"^(?=.*[!-~])[ -~]+$"}
monthly_overage_capNoThe most overage credits this key may take on per calendar month (UTC), or null to clear the cap. Omit it to keep the current cap. {"type":["integer","null"],"minimum":1}

Schema rules: {"type":"object","additionalProperties":false}.

ApiKeyWithSecret

One key exactly as creation answers it. This is the only shape that ever carries the secret.

FieldRequiredDescription and type
idYesThe key's public identifier: "key_" followed by 32 lowercase hexadecimal characters. {"type":"string","pattern":"^key_[0-9a-f]{32}$"}
nameYes{"type":"string"}
secretYesThe full secret: "thirds_sk_v1_" followed by 64 lowercase hexadecimal characters. It is shown here once and never again. {"type":"string","pattern":"^thirds_sk_v1_[0-9a-f]{64}$"}
display_prefixYesThe first characters of the secret, enough to tell keys apart in a list. {"type":"string"}
statusYes{"type":"string","enum":["active"]}
created_atYes{"type":"string","format":"date-time"}

Schema rules: {"type":"object","additionalProperties":false}.

ApiKeyList

One bounded page of the account's key history.

FieldRequiredDescription and type
dataYes{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/ApiKey"}}ApiKey
next_cursorYes{"type":["string","null"],"maxLength":128}

Schema rules: {"type":"object","additionalProperties":false}.

Shared errors, headers, and authentication schemes are in shared types and security.

Ready to make your first file?

Start in the playground with a gallery template, or get an API key and send your first request from your own terminal.