Account and API keys
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.
| Parameter | Location | Required | Rule |
|---|---|---|---|
x-csrf-token | header | Yes | {"type":"string"} |
Request body: application/json, AccountEmailRequest.
| Status | Meaning | Body |
|---|---|---|
| 202 | The request was accepted. This does not confirm mail delivery or whether another account holds the address. | application/json: AccountEmailAccepted |
| 400 | The JSON request is invalid or has unknown fields. | application/json: ErrorEnvelope |
| 401 | A live browser session is required. Expired, revoked, and suspended sessions are refused. | application/json: ErrorEnvelope |
| 403 | The CSRF token is missing or does not match this browser session. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 409 | This account already has an email address. The code is email_already_set. | application/json: ErrorEnvelope |
| 422 | The email address is invalid. The code is invalid_email. | application/json: ErrorEnvelope |
| 429 | The request limit was reached. Retry after the time in Retry-After. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | The request failed internally. | application/json: ErrorEnvelope |
| 503 | Email 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.
| Status | Meaning | Body |
|---|---|---|
| 201 | The key was created. This is the only response that ever carries the secret. | application/json: ApiKeyWithSecret |
| 400 | The name was empty, longer than 100 characters, or held a character that is not printable ASCII. | application/json: ErrorEnvelope |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 409 | The account already holds ten active keys. Revoke one before creating another. | application/json: ErrorEnvelope |
| 413 | The request body is larger than 16 KiB. | application/json: ErrorEnvelope |
| 415 | The request did not carry a JSON content type. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An 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.
| Parameter | Location | Required | Rule |
|---|---|---|---|
limit | query | No | The page size. Defaults to 20 and must be from 1 through 100. {"type":"integer","minimum":1,"maximum":100,"default":20} |
cursor | query | No | An opaque value from an earlier page's next_cursor. {"type":"string","maxLength":128} |
| Status | Meaning | Body |
|---|---|---|
| 200 | One page of the account's key history. | application/json: ApiKeyList |
| 400 | The page size or cursor is not valid. | application/json: ErrorEnvelope |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An 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.
| Parameter | Location | Required | Rule |
|---|---|---|---|
key_id | path | Yes | The key's public identifier, such as "key_1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^key_[0-9a-f]{32}$"} |
| Status | Meaning | Body |
|---|---|---|
| 200 | The key, now revoked. | application/json: ApiKey |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 404 | No 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 |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An 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.
| Parameter | Location | Required | Rule |
|---|---|---|---|
key_id | path | Yes | The key's public identifier, such as "key_1f8b3c7d5e2a49061f8b3c7d5e2a4906". {"type":"string","pattern":"^key_[0-9a-f]{32}$"} |
Request body: application/json, ApiKeyUpdate.
| Status | Meaning | Body |
|---|---|---|
| 200 | The key with its new cap. | application/json: ApiKey |
| 400 | The cap was zero, negative, not a whole number, or the body held a field this operation does not know. | application/json: ErrorEnvelope |
| 401 | The request did not carry a valid, active API key. | application/json: ErrorEnvelope |
| 404 | No 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 |
| 405 | The method is not allowed on this route. | application/json: ErrorEnvelope |
| 413 | The request body is larger than 16 KiB. | application/json: ErrorEnvelope |
| 415 | The request did not carry a JSON content type. | application/json: ErrorEnvelope |
| 431 | The request has more than 64 headers or more than 32 KiB of header names and values. | application/json: ErrorEnvelope |
| 500 | An internal error occurred. | application/json: ErrorEnvelope |
Schemas
AccountEmailRequest
| Field | Required | Description and type |
|---|---|---|
email | Yes | {"type":"string"} |
next | No | A 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.
| Field | Required | Description and type |
|---|---|---|
name | Yes | 1 to 100 printable ASCII characters. {"type":"string","minLength":1,"maxLength":100,"pattern":"^(?=.*[^ ])[ -~]+$"} |
Schema rules: {"type":"object","additionalProperties":false}.
ApiKey
One key, without its secret.
| Field | Required | Description and type |
|---|---|---|
id | Yes | The key's public identifier: "key_" followed by 32 lowercase hexadecimal characters. {"type":"string","pattern":"^key_[0-9a-f]{32}$"} |
name | Yes | {"type":"string"} |
display_prefix | Yes | The first characters of the secret, enough to tell keys apart in a list. {"type":"string"} |
status | Yes | {"type":"string","enum":["active","revoked"]} |
created_at | Yes | {"type":"string","format":"date-time"} |
revoked_at | Yes | {"type":["string","null"],"format":"date-time"} |
last_used_at | Yes | {"type":["string","null"],"format":"date-time"} |
monthly_overage_cap | Yes | The 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.
| Field | Required | Description and type |
|---|---|---|
name | No | New key name. The key secret and prefix stay the same. {"type":"string","minLength":1,"maxLength":100,"pattern":"^(?=.*[!-~])[ -~]+$"} |
monthly_overage_cap | No | The 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.
| Field | Required | Description and type |
|---|---|---|
id | Yes | The key's public identifier: "key_" followed by 32 lowercase hexadecimal characters. {"type":"string","pattern":"^key_[0-9a-f]{32}$"} |
name | Yes | {"type":"string"} |
secret | Yes | The 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_prefix | Yes | The first characters of the secret, enough to tell keys apart in a list. {"type":"string"} |
status | Yes | {"type":"string","enum":["active"]} |
created_at | Yes | {"type":"string","format":"date-time"} |
Schema rules: {"type":"object","additionalProperties":false}.
ApiKeyList
One bounded page of the account's key history.
| Field | Required | Description and type |
|---|---|---|
data | Yes | {"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/ApiKey"}} — ApiKey |
next_cursor | Yes | {"type":["string","null"],"maxLength":128} |
Schema rules: {"type":"object","additionalProperties":false}.
Shared errors, headers, and authentication schemes are in shared types and security.