Management API reference

Last updated 2 November 2026

View .md

Every operation of the Management API at https://api.tonbo.dev/v1, the API the tonbo CLI calls, generated from its OpenAPI document.

The document is public, at openapi.json and, as written, openapi.yaml: give it to an OpenAPI client generator or a coding agent. It also holds the operations of Tonbo Yield’s own client and retired ones kept for their callers, which this page leaves out.

Authentication

Every request carries a key as a bearer token, here from TONBO_API_KEY:

curl -sS https://api.tonbo.dev/v1/account \
  -H "Authorization: Bearer $TONBO_API_KEY"

A personal key (tbo_…) presented directly, or an access token exchanged from any key with the https://api.tonbo.dev audience. The key acts as its member with the member's current role and the key's narrowing, re-read on every request.

API keys and tokens explains how to create a key, the token exchange and what a key can reach.

Compatibility

Responses may gain fields; clients ignore fields they do not know. Request bodies stay closed: an unknown field is refused. A breaking change to an enforced operation ships as a new version, or with a compatibility window that names its consumer and removal condition.

Errors

A refusal answers application/problem+json, a Problem whose code names the refusal. The answers operations share:

AnswerStatusWhen
Conflict409Resource or idempotency conflict
Forbidden403The authenticated principal lacks the exact action
MachineRefused403, 409, 422, 429A refusal whose code and status are one of contracts/machines/machine-v2.json errors (such as name_taken, shape_not_offered, machine_failed or create_rate_limited), or a shared one such as region_not_offered (422, Machines are not offered in the region named or defaulted), forbidden, validation_error or rate_limited. A 429 (device_report_throttled, create_rate_limited, rate_limited) carries Retry-After.
MemberUnauthorized401Missing or invalid credentials (unauthenticated), or an Agent key presented directly (exchange_required): exchange it at token_endpoint and present the access token.
NotFound404Resource not found or outside the principal's Agent
PaymentRequired402The account's credit is exhausted (payment_required)
PreconditionFailed412The supplied If-Match value is stale (precondition_failed)
PreconditionRequired428The operation requires If-Match and the request has none (precondition_required, RFC 6585)
RateLimited429Request rate limit exceeded
RegionUnavailable503The region could not be reached or read; retry after Retry-After (region_unavailable). A region where the resource is not offered answers 422 region_not_offered instead.
ValidationError422Request validation failed

Account

Read the caller's account, membership and limits

GET /v1/account · CLI: tonbo account show

Responses

StatusAnswerBody
200The accountdata: Account
401MemberUnauthorizedProblem
403ForbiddenProblem
429RateLimitedProblem
Request
curl -sS "https://api.tonbo.dev/v1/account" \
  -H "Authorization: Bearer $TONBO_API_KEY"

List the regions new resources can be created in

GET /v1/regions · CLI: tonbo region list

Responses

StatusAnswerBody
200Regionsdata: array of Region
401MemberUnauthorizedProblem
403ForbiddenProblem
429RateLimitedProblem
Request
curl -sS "https://api.tonbo.dev/v1/regions" \
  -H "Authorization: Bearer $TONBO_API_KEY"

List the account's people and Agents with their roles

GET /v1/members · CLI: tonbo member list

Parameters

NameInTypeRequiredDescription
cursorquerystringnoOpaque cursor tied to the original query shape.
limitqueryintegerno

Responses

StatusAnswerBody
200Membersdata: array of Member, next_cursor: NextCursor
401MemberUnauthorizedProblem
403ForbiddenProblem
422ValidationErrorProblem
429RateLimitedProblem
Request
curl -sS "https://api.tonbo.dev/v1/members" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Change a member's role, or rename an Agent

PATCH /v1/members/{subject} · CLI: tonbo member update

Owners and admins give a person or an Agent another role; only an owner grants owner. name renames an Agent: it follows the Agent name rule and is unique among the account's active Agents (409 otherwise), and the Agent keeps its id, keys, home region and public hostname. A person's name belongs to their own profile, so name on a person is refused with 422 validation_error on the field name. Both fields in one request change together or not at all. People change members; an Agent's credential is refused with 403.

Parameters

NameInTypeRequiredDescription
subjectpathMemberSubjectyes

Request body

application/json, required.

UpdateMemberRequest

Responses

Request
curl -sS -X PATCH "https://api.tonbo.dev/v1/members/<subject>" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Create an Agent, an identity with a role in the account

POST /v1/agents · CLI: tonbo agent create

Owners and admins create Agents; only an owner grants owner. A replay with the same Idempotency-Key returns the Agent the first request created.

Parameters

NameInTypeRequiredDescription
Idempotency-Keyheaderstringyes

Request body

application/json, required.

CreateAccountAgentRequest

Responses

Request
curl -sS "https://api.tonbo.dev/v1/agents" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name":"<name>"}'

API keys

List your keys, or every key of the account for an owner or admin

GET /v1/api-keys · CLI: tonbo api-key list

Parameters

NameInTypeRequiredDescription
cursorquerystringnoOpaque cursor tied to the original query shape.
limitqueryintegerno

Responses

StatusAnswerBody
200Live keysdata: array of ApiKey, next_cursor: NextCursor
401MemberUnauthorizedProblem
403ForbiddenProblem
422ValidationErrorProblem
429RateLimitedProblem
Request
curl -sS "https://api.tonbo.dev/v1/api-keys" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Create a key; its secret is in this answer only

POST /v1/api-keys · CLI: tonbo api-key create

A personal key acts as you; an Agent key (member: agent.<agent id>), which owners and admins create, acts as the Agent, whose role may not exceed yours. read_only, and either buckets or any of databases, database_prefixes, streams and stream_prefixes, narrow the key for its whole life: a different narrowing takes a new key. An access token never creates a key, and neither does a narrowed key.

Request body

application/json, required.

CreateApiKeyRequest

Responses

Request
curl -sS "https://api.tonbo.dev/v1/api-keys" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"<name>"}'

Read one key

GET /v1/api-keys/{key_id} · CLI: tonbo api-key show

Parameters

NameInTypeRequiredDescription
key_idpathUuidyes

Responses

Request
curl -sS "https://api.tonbo.dev/v1/api-keys/<key_id>" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Revoke a key and every token minted from it

DELETE /v1/api-keys/{key_id} · CLI: tonbo api-key revoke

Your own key, or any key of the account for an owner or admin. A narrowed key may revoke itself (tonbo logout).

Parameters

NameInTypeRequiredDescription
key_idpathUuidyes

Responses

StatusAnswerBody
200The revocationdata: object
401MemberUnauthorizedProblem
403ForbiddenProblem
404NotFoundProblem
429RateLimitedProblem
Request
curl -sS -X DELETE "https://api.tonbo.dev/v1/api-keys/<key_id>" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Databases

List the account's databases

GET /v1/databases · CLI: tonbo database list

Live rows of the listed kinds (contracts/streams-databases/v1.json kinds whose api is databases: a member's databases and each Machine's database, with kind and the owning Machine as owner_id), creating, active or deleting, in every region, by name. A key narrowed to databases sees only the databases it reaches, and a key narrowed to buckets, or by name to streams alone, sees none. last_write_at and stored_bytes come from each region's meter; unavailable_regions names the regions whose sizes could not be read, whose databases then show null sizes.

Parameters

NameInTypeRequiredDescription
prefixqueryDatabasePrefixnoOnly databases whose name starts with this prefix.
cursorquerystringnoOpaque cursor tied to the original query shape.
limitqueryintegerno

Responses

StatusAnswerBody
200Databasesdata: array of Database, next_cursor: NextCursor, unavailable_regions: array of RegionId
401MemberUnauthorizedProblem
403ForbiddenProblem
422ValidationErrorProblem
429RateLimitedProblem
Request
curl -sS "https://api.tonbo.dev/v1/databases" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Create a database

POST /v1/databases · CLI: tonbo database create

Any member creates databases; a narrowed key never does. A name is unique within the account across every region while a database holds it, and is free again once that database is deleted. The region is fixed at creation; without one, the database goes to the region whose default is true. An optional expires_at, at least 60 seconds after the creation and fixed for the database's life, has Tonbo delete the database within a minute after it passes, as a deletion would; a replay is not held to the 60 seconds. Databases and streams expire by the same rule; a Machine's database never expires. The answer is 201 once the database is active and 202 while it is still being created; read it again until it is active. A replay with the same Idempotency-Key answers the same database; the key reused for another name, region or expiry answers 409. An account holds at most limits.databases_per_account live databases (contracts/streams-databases/v1.json); past that the answer is 403 and names whom to contact. An account whose credit is exhausted answers 402.

Parameters

NameInTypeRequiredDescription
Idempotency-Keyheaderstringyes

Request body

application/json, required.

CreateDatabaseRequest

Responses

StatusAnswerBody
201The database, activedata: Database
202The database, still being createddata: Database
401MemberUnauthorizedProblem
402PaymentRequiredProblem
403ForbiddenProblem
409ConflictProblem
422ValidationErrorProblem
429RateLimitedProblem
503RegionUnavailableProblem
Request
curl -sS "https://api.tonbo.dev/v1/databases" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name":"<name>"}'

Read a database

GET /v1/databases/{name} · CLI: tonbo database show

A live database of the account, with its url, expires_at and state; clients that open a database by name, such as @tonbo/sqlite-ursula/tonbo, resolve it here. A key narrowed to databases reads the databases it names and those under its prefixes, and any other name answers 404 with the same status and body as a database that does not exist, so the narrowing does not disclose whether the account's other databases exist. A key narrowed to buckets, or by name to streams alone, reads none.

Parameters

NameInTypeRequiredDescription
namepathListedDatabaseNameyes

Responses

Request
curl -sS "https://api.tonbo.dev/v1/databases/<name>" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Publish a database for anonymous reads, or take it private

PATCH /v1/databases/{name} · CLI: tonbo database update

Owners and admins, on an active database. Anonymous readers reach only the database's current incarnation. A Machine's database answers 409 managed_by_machine (contracts/streams-databases/v1.json kinds.managed_refusal).

Parameters

NameInTypeRequiredDescription
namepathListedDatabaseNameyes
If-MatchheaderstringnoOptional conditional update; omitted requests use last-write-wins.

Request body

application/json, required.

UpdateDatabaseRequest

Responses

Request
curl -sS -X PATCH "https://api.tonbo.dev/v1/databases/<name>" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"public_read":false}'

Delete a database and everything in it

DELETE /v1/databases/{name} · CLI: tonbo database delete

Any member deletes databases; a narrowed key never does. The answer is 200 once the database is deleted (state is deleted) and 202 while it is still being deleted. Its name is free again once it is deleted; its usage this month stays in /usage/databases. Repeating the request answers the database being deleted. A Machine's database is deleted with its Machine and answers 409 managed_by_machine (contracts/streams-databases/v1.json kinds.managed_refusal).

Parameters

NameInTypeRequiredDescription
namepathListedDatabaseNameyes

Responses

StatusAnswerBody
200The database, deleteddata: Database
202The database, being deleteddata: Database
401MemberUnauthorizedProblem
403ForbiddenProblem
404NotFoundProblem
429RateLimitedProblem
Request
curl -sS -X DELETE "https://api.tonbo.dev/v1/databases/<name>" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Streams

List the account's streams

GET /v1/streams · CLI: tonbo stream list

Live raw streams (contracts/streams-databases/v1.json kinds whose api is streams), creating, active or deleting, in every region, by name. A key narrowed to streams sees only the streams it reaches, and a key narrowed to buckets, or by name to databases alone, sees none. last_write_at and stored_bytes come from each region's meter; unavailable_regions names the regions whose sizes could not be read, whose streams then show null sizes.

Parameters

NameInTypeRequiredDescription
prefixqueryStreamPrefixnoOnly streams whose name starts with this prefix.
cursorquerystringnoOpaque cursor tied to the original query shape.
limitqueryintegerno

Responses

StatusAnswerBody
200Streamsdata: array of Stream, next_cursor: NextCursor, unavailable_regions: array of RegionId
401MemberUnauthorizedProblem
403ForbiddenProblem
422ValidationErrorProblem
429RateLimitedProblem
Request
curl -sS "https://api.tonbo.dev/v1/streams" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Create a stream

POST /v1/streams · CLI: tonbo stream create

Any member creates streams; a narrowed key never does. A name is unique among the account's streams across every region while a stream holds it, and is free again once that stream is deleted; a stream and a database may share a name. The region is fixed at creation; without one, the stream goes to the region whose default is true for databases, since a stream lives in the same account bucket. The content type is fixed at creation (application/octet-stream when omitted) and every append must send exactly it. An optional expires_at, at least 60 seconds after the creation and fixed for the stream's life, has Tonbo delete the stream within a minute after it passes, as a deletion would; a replay is not held to the 60 seconds. The answer is 201 once the stream is active and 202 while it is still being created; read it again until it is active. A replay with the same Idempotency-Key answers the same stream; the key reused for another name, region, content type or expiry answers 409. An account holds at most limits.streams_per_account live streams (contracts/streams-databases/v1.json), counted apart from its databases; past that the answer is 403 and names whom to contact. An account whose credit is exhausted answers 402.

Parameters

NameInTypeRequiredDescription
Idempotency-Keyheaderstringyes

Request body

application/json, required.

CreateStreamRequest

Responses

StatusAnswerBody
201The stream, activedata: Stream
202The stream, still being createddata: Stream
401MemberUnauthorizedProblem
402PaymentRequiredProblem
403ForbiddenProblem
409ConflictProblem
422ValidationErrorProblem
429RateLimitedProblem
503RegionUnavailableProblem
Request
curl -sS "https://api.tonbo.dev/v1/streams" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name":"<name>"}'

Read a stream

GET /v1/streams/{name} · CLI: tonbo stream show

A live stream of the account, with its url, content_type, expires_at and state. A key narrowed to streams reads the streams it names and those under its prefixes; a key narrowed to buckets, or by name to databases alone, reads none. Any name a key does not reach answers 404 with the same status and body as a stream that does not exist.

Parameters

NameInTypeRequiredDescription
namepathStreamNameyes

Responses

Request
curl -sS "https://api.tonbo.dev/v1/streams/<name>" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Publish a stream for anonymous reads, or take it private

PATCH /v1/streams/{name} · CLI: tonbo stream update

Owners and admins, on an active stream. Anonymous readers reach only the stream's current incarnation, with GET and HEAD (reads, long-poll and SSE tails); they never append.

Parameters

NameInTypeRequiredDescription
namepathStreamNameyes
If-MatchheaderstringnoOptional conditional update; omitted requests use last-write-wins.

Request body

application/json, required.

UpdateStreamRequest

Responses

Request
curl -sS -X PATCH "https://api.tonbo.dev/v1/streams/<name>" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"public_read":false}'

Delete a stream and everything in it

DELETE /v1/streams/{name} · CLI: tonbo stream delete

Any member deletes streams; a narrowed key never does. The answer is 200 once the stream is deleted (state is deleted) and 202 while it is still being deleted. Its name is free again once it is deleted; its usage this month stays in /usage/streams. Repeating the request answers the stream being deleted.

Parameters

NameInTypeRequiredDescription
namepathStreamNameyes

Responses

StatusAnswerBody
200The stream, deleteddata: Stream
202The stream, being deleteddata: Stream
401MemberUnauthorizedProblem
403ForbiddenProblem
404NotFoundProblem
429RateLimitedProblem
Request
curl -sS -X DELETE "https://api.tonbo.dev/v1/streams/<name>" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Usage

Read the account's rated usage of one month

GET /v1/usage · CLI: tonbo usage show

Parameters

NameInTypeRequiredDescription
periodquerystringnoThe calendar month in UTC; the current month when omitted.

Responses

Request
curl -sS "https://api.tonbo.dev/v1/usage" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Read each database's usage of one month at list price

GET /v1/usage/databases · CLI: tonbo database usage

One row per listed database version (name, stream creation time and counting history) metered in the month, rolled up by contracts/streams-databases/v1.json kinds.values.<kind>.usage: a Machine's logs and inbox streams are added into its database's row, and a Volume's ledger into other; in every region the account reaches, including databases deleted during the month, plus other for metered usage outside the account's databases (its streams, which /usage/streams lists, session processing and anything unattributed), so the rows and other add up to the account's metered total. Values are at list price, before the monthly allowance and credit; Usage & billing (/usage) shows what is charged.

Parameters

NameInTypeRequiredDescription
periodquerystringnoThe calendar month in UTC; the current month when omitted.
sortqueryone of "-list_value" or "name"noBy list value, highest first (the default), or by name.
prefixqueryDatabasePrefixnoOnly databases whose name starts with this prefix.
namesquerystringnoOnly these databases, 1 to 100 listed names, comma-separated (a database name or a Machine's database).
cursorquerystringnoOpaque cursor tied to the original query shape.
limitqueryintegerno

Responses

Request
curl -sS "https://api.tonbo.dev/v1/usage/databases" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Read each stream's usage of one month at list price

GET /v1/usage/streams · CLI: tonbo stream usage

One row per stream version (name, stream creation time and counting history) metered in the month, in every region the account reaches, including streams deleted during the month, plus other for metered usage outside the account's streams (its databases, session processing and anything unattributed), so the rows and other add up to the account's metered total, as /usage/databases does for databases; no stream is a row of both. Values are at list price, before the monthly allowance and credit, which databases and streams share; Usage & billing (/usage) shows what is charged.

Parameters

NameInTypeRequiredDescription
periodquerystringnoThe calendar month in UTC; the current month when omitted.
sortqueryone of "-list_value" or "name"noBy list value, highest first (the default), or by name.
prefixqueryStreamPrefixnoOnly streams whose name starts with this prefix.
namesquerystringnoOnly these streams, 1 to 100 stream names, comma-separated.
cursorquerystringnoOpaque cursor tied to the original query shape.
limitqueryintegerno

Responses

Request
curl -sS "https://api.tonbo.dev/v1/usage/streams" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Machines Not open yet

Machines are not open yet. These pages describe Machines as they will open; until then, a create answers region_not_offered (422), “Machines are not offered in any region yet.”

List the account's Machines

GET /v1/machines · CLI: tonbo machine list

The account's Machines by name, except those being deleted: managed Machines and, in a personal account, its devices (kind byoc, which PUT /machines/self registers) from every region, whatever machines.open says; unavailable_regions names the regions whose Machines on the page could not be read. A device answers id, name, kind, region, byoc (with its key_state, derived from its current login key), created_at and updated_at; its byoc.hostname and byoc.device_id are answered to the device's person only, never to an Agent key. A narrowed key never reaches a Machine. An organization holds managed Machines only (contracts/machines/machine-v2.json kinds_by_account_kind), so its list never answers a byoc Machine or its fields, and its pages and cursors are those of its managed Machines alone.

Parameters

NameInTypeRequiredDescription
cursorquerystringnoOpaque cursor tied to the original query shape.
limitqueryintegerno

Responses

StatusAnswerBody
200Machinesdata: array of Machine, next_cursor: NextCursor, unavailable_regions: array of RegionId
401MemberUnauthorizedProblem
403ForbiddenProblem
422MachineRefusedProblem
429RateLimitedProblem
Request
curl -sS "https://api.tonbo.dev/v1/machines" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Create a Machine

POST /v1/machines · CLI: tonbo machine create

Any member creates Machines (machines.write); a public port also needs machines.public, and a narrowed key never does. The image is the reserved platform reference tonbo.dev/platform (machine-v2.schema.json $defs.platform_image_reference), which resolves nothing, is stored with repository and digest null and reads as state ready, or a public registry reference, which resolves to its canonical repository and linux/arm64 digest, stored with the reference, and reads as state pulling until a node has pulled it (state is computed, never stored; see getMachine); a registry reference is refused before anything is written, with 422 image_platform_unsupported when it has no linux/arm64 manifest and validation_error when it is not a public reference the resolver can fetch under machine-v2.json image.distribution (a malformed reference, an unknown or unavailable image, a redirected manifest or an oversized document). The name, the HTTPS label <name>-<account> and the SSH username are then reserved for the account, and the Machine is recorded in its region, which is fixed for its life; without region it goes to the account's default creation region. A region where Machines are not offered (machines.open in contracts/regions/cells-v1.json) answers the shared 422 region_not_offered, and a shape that is not offered 422 shape_not_offered, before anything is written. The answer is 202 while the Machine is being created and 201 in every other state (machine-v2.json creation.create_status). Both carry Location, the Machine's URL, and the 202 also Retry-After, the seconds to wait before reading the Machine there again (machine-v2.json creation.poll_after_seconds), until its state is active or failed. A replay with the same Idempotency-Key answers the same Machine as it is now, so a replay of a Machine whose creation failed answers 201 with its failure; the key reused for another request answers 409 idempotency_conflict. Refusals carry the codes and statuses of contracts/machines/machine-v2.json errors.

Parameters

NameInTypeRequiredDescription
Idempotency-Keyheaderstringyes

Request body

application/json, required.

machines/machine-v2.schema.json#/$defs/create

Responses

StatusAnswerBody
201The Machine, no longer being created (a replay of a failed create carries its failure)MachineEnvelope
202The Machine, still being createdMachineEnvelope
401MemberUnauthorizedProblem
403MachineRefusedProblem
409MachineRefusedProblem
422MachineRefusedProblem
429MachineRefusedProblem
Request
curl -sS "https://api.tonbo.dev/v1/machines" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d @request.json

Read a Machine

GET /v1/machines/{name} · CLI: tonbo machine show

A Machine of the account. A device (kind byoc, in a personal account only) is served whatever machines.open says and answers its byoc facts, hostname and device_id to its person only, with its id as its ETag ("<id>"), which a delete may name in If-Match. A managed Machine's ETag is "<id>.<revision>" (contracts/machines/machine-v2.json etag), opaque to clients: an update names it in If-Match, and a delete or a sleep may, so a tag read from one Machine never matches another that later takes its name. A Machine is read whatever machines.open says, so a closure never hides it; a region this server cannot reach answers 503 region_unavailable. Its image names the canonical repository a registry image is pulled from and a state computed from the pull: pulling until a node has pulled the digest while the Machine is creating or active, ready once one has (always for the platform reference tonbo.dev/platform, which is never pulled), failed with a failure whose code is one of contracts/machines/machine-v2.json image.failures, and not_pulled on a failed or deleting Machine that no node pulled, since nothing will. While the image is not ready, a request that would wake the Machine answers 503 image_pulling, or 409 image_failed once the pull failed. status.conditions says why a Machine is not doing what it should: wake_blocked when its last wake was refused and none has succeeded since, with the reason, since and, for a crash backoff, retry_at; creation_delayed once it has been creating for longer than contracts/machines/machine-v2.json creation.alert_after_seconds; egress_blocked while its outbound access is blocked; and volume_purged when the Volume its spec names was purged (in practice after 30 days of suspension past the owners' notice), so it boots without it until the spec names another Volume or none. activation.revision is the spec revision the live activation was admitted with. Every spec change advances the Machine's revision, but ports and sleep_after_seconds apply at once, so a lower activation.revision means a sleep is needed only when image, command, shape or env changed; GET /machines/{name}/activations says how each earlier activation ended. A managed Machine names its creator in created_by.

Parameters

NameInTypeRequiredDescription
namepathmachines/machine-v2.schema.json#/$defs/nameyes

Responses

Request
curl -sS "https://api.tonbo.dev/v1/machines/<name>" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Change a Machine's spec

PATCH /v1/machines/{name} · CLI: tonbo machine update

A managed Machine only: a device answers 409 machine_kind_mismatch, whatever machines.open says. If-Match is required, because env and ports are replaced whole: the change is written only at the revision it names, so a missing one answers 428 precondition_required, a stale one 412 precondition_failed, and a concurrent change is never lost; an If-Match that is not this Machine's ETag, another Machine's that had the name included, also answers 412 precondition_failed, and so does a bare revision ("<n>"), whose detail says to upgrade @tonbo/cli to 0.9.0 or later (machine-v2.json etag.bare_revision). A Machine whose creation failed answers 409 machine_failed and does not change. A region where Machines are not offered answers 422 region_not_offered. A change to image, command, shape or env applies at the next activation; ports and sleep_after_seconds apply at once. A changed image follows the create's rule: the platform reference tonbo.dev/platform resolves nothing, and any other reference resolves to its canonical repository and linux/arm64 digest, or is refused before anything is written as the create refuses it (422 image_platform_unsupported or validation_error). Naming an image, the same one included, resolves it again and returns its state to pulling, so a failed pull is retried. Making a port public also needs machines.public. Refusals carry the codes and statuses of contracts/machines/machine-v2.json errors.

Parameters

NameInTypeRequiredDescription
namepathmachines/machine-v2.schema.json#/$defs/nameyes
If-MatchheaderstringyesThe Machine's ETag as read, "<id>.<revision>" (contracts/machines/machine-v2.json etag); a Machine update is written only to that Machine at that revision. Required, because env and ports are replaced whole; a missing one answers 428 precondition_required.

Request body

application/json, required.

machines/machine-v2.schema.json#/$defs/update

Responses

Request
curl -sS -X PATCH "https://api.tonbo.dev/v1/machines/<name>" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "If-Match: <if-match>" \
  -H "Content-Type: application/json" \
  -d @request.json

Delete a Machine

DELETE /v1/machines/{name} · CLI: tonbo machine delete

A managed Machine stops and is purged with its database and logs; its name is free at once for the account's next Machine, while its hostnames stay reserved to the account. The answer is 202 with the Machine being deleted, after which it is absent from the API, or 204 with no body when the name's create never reached its region and only the name was released. A region where Machines are not offered answers 422 region_not_offered, and one this server cannot reach 503 region_unavailable. A device (kind byoc) is forgotten instead, whatever machines.open says (contracts/machines/machine-v2.json kinds.byoc.forget): it is hidden and leaves the per-person cap at once, and its name is free once the forget drains; the answer is 202 with the device as it was, or 204 when its region held no row for it; a 204 names the kind in X-Tonbo-Machine-Kind. Forgetting a device that is not the caller's own (an Agent key's) also needs api_keys.others (actions delete byoc_requires_also), 403 otherwise. A device's key is not revoked: a device whose key is still active registers again at its next start. If-Match is optional; when sent it must be the Machine's current ETag (a device's is its id), or the answer is 412 precondition_failed and nothing is deleted, so a delete retried after the name was reused never deletes the new Machine. A name whose create never reached its region has no ETag to match.

Parameters

NameInTypeRequiredDescription
namepathmachines/machine-v2.schema.json#/$defs/nameyes
If-MatchheaderstringnoThe Machine's ETag as read (contracts/machines/machine-v2.json etag); when sent, the request acts only on that Machine at that revision, and otherwise answers 412 precondition_failed.

Responses

StatusAnswerBody
202The Machine, being deleted, or the device, forgottenMachineEnvelope
204The name was released, no Machine having been recorded under it, or the device was forgotten, its region holding no row for itnone
401MemberUnauthorizedProblem
403ForbiddenProblem
404NotFoundProblem
412PreconditionFailedProblem
422MachineRefusedProblem
429RateLimitedProblem
503RegionUnavailableProblem
Request
curl -sS -X DELETE "https://api.tonbo.dev/v1/machines/<name>" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Put a Machine to sleep now

POST /v1/machines/{name}/sleep · CLI: tonbo machine sleep

machine-controller applies the Machine's effective idle action (status.sleep). An asleep Machine wakes on its next connection; there is no wake operation. The answer is 202 while an activation runs, with Retry-After, the seconds to wait before reading the Machine again (machine-v2.json sleep.poll_after_seconds), until its status.power is asleep; and 200 with the Machine unchanged when there is nothing to do: it is asleep, or still being created. A Machine whose creation failed answers 409 machine_failed, and a device 409 machine_kind_mismatch, whatever machines.open says. A region where Machines are not offered answers 422 region_not_offered. If-Match is optional; when sent it must be the Machine's current ETag, or the answer is 412 precondition_failed and nothing changes.

Parameters

NameInTypeRequiredDescription
namepathmachines/machine-v2.schema.json#/$defs/nameyes
If-MatchheaderstringnoThe Machine's ETag as read (contracts/machines/machine-v2.json etag); when sent, the request acts only on that Machine at that revision, and otherwise answers 412 precondition_failed.

Responses

Request
curl -sS -X POST "https://api.tonbo.dev/v1/machines/<name>/sleep" \
  -H "Authorization: Bearer $TONBO_API_KEY"

List a Machine's activations

GET /v1/machines/{name}/activations · CLI: tonbo machine activations

A managed Machine's activations, newest first: each boot or refused wake, with what woke it, the spec revision and shape it was admitted with, when, and its end (machine-v2.schema.json $defs.activation_end), null while it is live. An end's reason is why it ended: a refusal such as payment_required or crash_backoff, a stop such as idle_early, sleep_after, workload_exited (with the exit_code or signal) or funding_stop, or the node's. Any member reads it (account.read), whatever machines.open says; a narrowed key never does. A device answers 409 machine_kind_mismatch, and a region this server cannot reach 503 region_unavailable. The cursor is tied to the Machine, so it never pages another Machine that later takes the name.

Parameters

NameInTypeRequiredDescription
namepathmachines/machine-v2.schema.json#/$defs/nameyes
cursorquerystringnoOpaque cursor tied to the original query shape.
limitqueryintegerno

Responses

StatusAnswerBody
200Activations, newest firstdata: array of machines/machine-v2.schema.json#/$defs/activation_entry, next_cursor: NextCursor
401MemberUnauthorizedProblem
403ForbiddenProblem
404NotFoundProblem
409MachineRefusedProblem
422MachineRefusedProblem
429RateLimitedProblem
503RegionUnavailableProblem
Request
curl -sS "https://api.tonbo.dev/v1/machines/<name>/activations" \
  -H "Authorization: Bearer $TONBO_API_KEY"

List the Machine shapes

GET /v1/machine-shapes · CLI: tonbo machine shapes

Every shape of contracts/machines/shapes-v1.json, with whether a Machine may name it now and its list price for an hour at full use, and the shape a client proposes by default. Served to any member whatever machines.open says; a narrowed key never reaches it.

Responses

StatusAnswerBody
200Shapesdata: array of MachineShape, default: string
401MemberUnauthorizedProblem
403ForbiddenProblem
429RateLimitedProblem
Request
curl -sS "https://api.tonbo.dev/v1/machine-shapes" \
  -H "Authorization: Bearer $TONBO_API_KEY"

Schemas

Account

FieldTypeRequiredDescription
idUuidyes
kindone of "user" or "organization"yes
namestringyes
memberobjectyesThe caller's own membership.
member.kindone of "user" or "agent"yes
member.subjectMemberSubjectyes
member.roleAccountRoleyes
limitsobjectyes
limits.bucketsintegeryesLive buckets the account may hold.

AccountAgent

FieldTypeRequiredDescription
idUuidyes
subjectMemberSubjectyes
namestringyes
roleAccountRoleyes
regionstringyes
created_atTimestampyes

AccountRole

one of "owner", "admin" or "member"

AgentName

The Agent's one name, defined by contracts/agents/agent-name-v1.json. It is unique among active Agents within its account and an owner or admin may change it (updateMember); there is no separate display name or slug.

string

ApiKey

FieldTypeRequiredDescription
idUuidyes
namestringyes
memberMemberSubjectyes
member_kindone of "user" or "agent"yes
last4stringyes
read_onlybooleanyes
bucketsarray of stringyes
databasesarray of DatabaseNameyes
database_prefixesarray of DatabasePrefixyes
streamsarray of StreamNameyes
stream_prefixesarray of StreamPrefixyes
expires_atTimestamp or nullyes
last_used_atTimestamp or nullyesRecorded at most once a minute.
created_atTimestampyes

ApiKeyExpiration

one of "15_days", "1_month", "6_months", "1_year" or "never"

ApiKeyWithSecret

Every field of ApiKey, and:

FieldTypeRequiredDescription
keystringyesThe secret, shown once. tbo_ followed by 50 base-58 characters, the last six a checksum.

ContentType

A stream's content type (contracts/streams-databases/v1.json content_type): one lowercase media type type/subtype without parameters, at most 127 bytes, never under application/vnd.ursula. (the formats Tonbo manages). It is the form Ursula normalizes a content type to, so every append sends exactly it; application/json streams are in JSON mode.

string

CreateAccountAgentRequest

FieldTypeRequiredDescription
nameAgentNameyes
roleAccountRoleno
regionstringnoThe Agent's home region; the only open region when omitted.

CreateApiKeyRequest

FieldTypeRequiredDescription
namestringyes
memberMemberSubjectnoOmit for your own personal key; agent.<agent id> for an Agent key.
read_onlybooleannoNarrow the key to read-only actions, for its whole life.
bucketsarray of stringnoNarrow the key to these registered buckets of the account, for its whole life. Never with databases, database_prefixes, streams or stream_prefixes.
databasesarray of DatabaseNamenoNarrow the key to these databases, for its whole life; a name need not exist yet. Never with buckets.
database_prefixesarray of DatabasePrefixnoNarrow the key to the databases whose name starts with one of these, for its whole life. Never with buckets.
streamsarray of StreamNamenoNarrow the key to these raw streams, by the name the streams API shows, for its whole life; a name need not exist yet. Never with buckets. A key narrowed to both databases and streams reaches both.
stream_prefixesarray of StreamPrefixnoNarrow the key to the raw streams whose name starts with one of these, for its whole life; each at most 83 bytes, the longest stream name. Never with buckets.
expires_inApiKeyExpirationno

CreateDatabaseRequest

FieldTypeRequiredDescription
nameDatabaseNameyes
regionRegionIdnoThe database's region, fixed for its life; the region whose default is true when omitted. A region closed to databases answers 503.
expires_atTimestampnoWhen the database expires, an RFC 3339 time, fixed for its life; it never expires when omitted. A new database's expiry must be at least 60 seconds after its creation by the registry's clock (422 on expires_at otherwise); a replay of the Idempotency-Key must name the same time and is answered however near or past that time is. Once it has passed, Tonbo deletes the database within a minute, as a deletion would.

CreateStreamRequest

FieldTypeRequiredDescription
nameStreamNameyes
regionRegionIdnoThe stream's region, fixed for its life; the region whose default is true when omitted. A region closed to databases, and so to streams, answers 503.
content_typeContentTypenoThe stream's content type, fixed for its life; application/octet-stream when omitted.
expires_atTimestampnoWhen the stream expires, an RFC 3339 time, fixed for its life; it never expires when omitted. A new stream's expiry must be at least 60 seconds after its creation by the registry's clock (422 on expires_at otherwise); a replay of the Idempotency-Key must name the same time and is answered however near or past that time is. Once it has passed, Tonbo deletes the stream within a minute, as a deletion would.

Database

FieldTypeRequiredDescription
nameListedDatabaseNameyes
kindDatabaseKindyes
owner_idUuid or nullyesThe Machine that owns a machine row; null for a member's database.
regionRegionIdyes
urlstring (uri)yesThe database's data-plane address, {region's database origin}/{account id}/{name}; open it with the SQLite on Ursula client.
expires_atTimestamp or nullyesWhen the database expires, fixed at creation; null when it never expires, and always for a Machine's database. Once it has passed, Tonbo deletes the database within a minute, as a deletion would, and its name is free again.
incarnationstring or nullyesUrsula's Stream-Incarnation as a decimal; null while creating. A client that cached the URL resolves the database again when it changes.
stateone of "creating", "active", "deleting" or "deleted"yesdeleted appears only in the answer to the deletion that completed it.
public_readbooleanyesWhether anonymous reads are served.
created_atTimestampyes
created_byMemberSubjectyes
last_write_atTimestamp or nullyesThe last write the region's meter saw, up to a minute behind; null until a host first opens it (an open writes; the create does not), and when its region is unavailable.
stored_bytesobjectyesWhat the region's meter last saw stored; null when not yet reported or unavailable.
stored_bytes.loginteger or nullyes
stored_bytes.snapshotinteger or nullyes
measured_atTimestamp or nullnoWhen the region's meter last read last_write_at and stored_bytes (every minute while it is healthy); null when the database is not yet metered or its region is unavailable. Optional; absent from older answers.
writesone of "accepting", "suspended" or nullnoWhether Tonbo accepts writes to the database now. suspended while the account has no credit left, when writes answer 402 PAYMENT_REQUIRED and reads continue; writes resume shortly after a top-up. It is the account's state, the same for each of its databases and streams. Only in the answer for one database (show and update), never in a list; null when it could not be read.

DatabaseKind

The registry kind of a listed row (contracts/streams-databases/v1.json kinds). A machine row is managed by its Machine; deleting it or changing its visibility answers 409 managed_by_machine.

one of "database" or "machine"

DatabaseName

A database's name (contracts/streams-databases/v1.json name): 1 to 85 lowercase letters, digits, dots, underscores and hyphens, starting and ending with a letter or digit, without ..; m., vol., s., session. and proc. are reserved, and streams and new (the console's create page) are not names. It appears unencoded in the database's URL and in key narrowing.

string

DatabasePrefix

A database name prefix (contracts/streams-databases/v1.json prefix); it selects every name that starts with it.

string

DatabaseUsage

Every field of DatabaseUsageAmounts, and:

FieldTypeRequiredDescription
nameListedDatabaseNameyes
kindDatabaseKindyes
owner_idUuid or nullyesThe Machine that owns a machine row, whose logs and inbox streams are added into it; null for a member's database.
regionRegionIdyes
stream_created_atTimestampyesWhen Ursula created the stream version; with counted_since, the version the row is.
counted_sincestringyesThe Ursula usage history the version began counting in, 16 hex digits.
stateone of "active" or "deleted"yes
created_atTimestamp or nullyesNull for a metered version the registry does not know.
deleted_atTimestamp or nullyes

DatabaseUsageAmounts

FieldTypeRequiredDescription
write_unitsstringyesWrite units, an integer as text.
retention_gib_monthsstringyesThe retained log in GiB-months (GiB = 1024^3 bytes, month = retention_month_seconds), a decimal as text.
snapshot_gib_monthsstring or nullyesSnapshots in GiB-months, a decimal as text; null when never reported.
list_value_microsintegeryesThe value at list price in micro-dollars, before allowance and credit.

DatabaseUsagePage

FieldTypeRequiredDescription
periodstringyes
as_ofTimestamp or nullyesThe oldest meter checkpoint among the regions read; usage after it is not yet counted. Null before a region's first.
currency"usd"yes
basis"at_list_price_before_allowance_and_credit"yes
pricingobjectyesThe list prices of contracts/billing/streams-prices-v1.json the values use.
pricing.write_unit_bytesintegeryes
pricing.write_micros_per_million_unitsintegeryes
pricing.retention_micros_per_gib_monthintegeryes
pricing.retention_unit_bytesintegeryes
pricing.retention_month_secondsintegeryes
dataarray of DatabaseUsageyes
otherDatabaseUsageAmountsyesMetered usage outside the account's databases (its streams, session processing and anything unattributed), in every region read.
next_cursorNextCursoryes
unavailable_regionsarray of RegionIdyesRegions whose usage could not be read; their databases and other are missing from this answer.

ListedDatabaseName

The name of a row the databases API lists (contracts/streams-databases/v1.json kinds whose api is databases): a member's database or a Machine's.

DatabaseName or MachineDatabaseName

Machine

A Machine as contracts/machines/machine-v2.schema.json defines it.

machines/machine-v2.schema.json

MachineDatabaseName

A Machine's database, m.<machine id> (contracts/streams-databases/v1.json kinds.values.machine.name_pattern). Control creates and deletes it with its Machine.

string

MachineEnvelope

FieldTypeRequiredDescription
dataMachineyes

MachineShape

A shape of contracts/machines/shapes-v1.json shapes, whether a Machine may name it now, and its list price.

FieldTypeRequiredDescription
shapemachines/machine-v2.schema.json#/$defs/shapeyes
vcpusintegeryes
memory_gibnumberyes
scratch_gibnumberyes
offeredbooleanyes
suspendablebooleanyes
ceiling_usd_micros_per_hourintegeryesThe shape's list price for an hour at full use, in micros of USD: its vCPUs, memory and scratch disk at contracts/billing/machine-prices-v1.json's hourly prices. Billing charges what a Machine uses, never more than this. The same figure an exec or file answer names after a wake.

Member

FieldTypeRequiredDescription
kindone of "user" or "agent"yes
subjectMemberSubjectyes
roleAccountRoleyes
namestring or nullyes
display_namestring or nullyes
created_atTimestampyes

MemberSubject

A person's user id, or agent.<agent id> for an Agent; the sub of its access tokens.

string

NextCursor

Opaque cursor of the next page; null on the last page.

string or null

Problem

FieldTypeRequiredDescription
typestring (uri)yes
titlestringyes
statusintegeryes
codestringyes
request_idstringyes
detailstringnoExplanation specific to this occurrence, when one exists.
failure_codeTurnFailureCode or nullnoPresent only on turn_failed; null for a failure recorded without a code.
token_endpointstring (uri)noPresent only on exchange_required; where to exchange the key.
errorsarray of ValidationIssueno

Region

FieldTypeRequiredDescription
idRegionIdyes
urlstring (uri)yesThe origin of the data plane in this region; a database's URL is this origin, the account id and the database name, and a bucket's is this origin and the bucket name.
defaultbooleanyesWhether a database goes to this region when its creation names none.

RegionId

string

Stream

FieldTypeRequiredDescription
nameStreamNameyes
regionRegionIdyes
urlstring (uri)yesThe stream's data-plane address, {region's Streams origin}/{account id}/s.{name} (contracts/streams-databases/v1.json stream_url); append, read, tail and close it with any Durable Streams client.
content_typeContentTypeyes
expires_atTimestamp or nullyesWhen the stream expires, fixed at creation; null when it never expires. Once it has passed, Tonbo deletes the stream within a minute, as a deletion would, and its name is free again.
incarnationstring or nullyesUrsula's Stream-Incarnation as a decimal; null while creating. It changes when a stream of the same name is deleted and created again.
stateone of "creating", "active", "deleting" or "deleted"yesdeleted appears only in the answer to the deletion that completed it.
public_readbooleanyesWhether anonymous reads are served.
created_atTimestampyes
created_byMemberSubjectyes
last_write_atTimestamp or nullyesThe last write the region's meter saw, up to a minute behind; null before the first append (the create is not one), and when its region is unavailable.
stored_bytesobjectyesWhat the region's meter last saw stored; null when not yet reported or unavailable.
stored_bytes.loginteger or nullyes
stored_bytes.snapshotinteger or nullyes
measured_atTimestamp or nullnoWhen the region's meter last read last_write_at and stored_bytes (every minute while it is healthy); null when the stream is not yet metered or its region is unavailable. Optional; absent from older answers.
writesone of "accepting", "suspended" or nullnoWhether Tonbo accepts writes to the stream now. suspended while the account has no credit left, when writes answer 402 PAYMENT_REQUIRED and reads continue; writes resume shortly after a top-up. It is the account's state, the same for each of its databases and streams. Only in the answer for one stream (show and update), never in a list; null when it could not be read.

StreamName

A stream's name (contracts/streams-databases/v1.json kinds.values.stream.name_pattern's capture group): the database name rule with at most 83 bytes, since its stream id is s. and the name. 1 to 83 lowercase letters, digits, dots, underscores and hyphens, starting and ending with a letter or digit, without ..; m., vol., s., session. and proc. are reserved, and streams and new are not names. It appears unencoded in the stream's URL after s..

string

StreamPrefix

A stream name prefix, under the name prefix rule (contracts/streams-databases/v1.json prefix); it selects every stream whose name starts with it.

string

StreamUsage

Every field of DatabaseUsageAmounts, and:

FieldTypeRequiredDescription
nameStreamNameyes
regionRegionIdyes
stream_created_atTimestampyesWhen Ursula created the stream version; with counted_since, the version the row is.
counted_sincestringyesThe Ursula usage history the version began counting in, 16 hex digits.
stateone of "active" or "deleted"yes
created_atTimestamp or nullyesNull for a metered version the registry does not know.
deleted_atTimestamp or nullyes

StreamUsagePage

FieldTypeRequiredDescription
periodstringyes
as_ofTimestamp or nullyesThe oldest meter checkpoint among the regions read; usage after it is not yet counted. Null before a region's first.
currency"usd"yes
basis"at_list_price_before_allowance_and_credit"yes
pricingobjectyesThe list prices of contracts/billing/streams-prices-v1.json the values use.
pricing.write_unit_bytesintegeryes
pricing.write_micros_per_million_unitsintegeryes
pricing.retention_micros_per_gib_monthintegeryes
pricing.retention_unit_bytesintegeryes
pricing.retention_month_secondsintegeryes
dataarray of StreamUsageyes
otherDatabaseUsageAmountsyesMetered usage outside the account's streams (its databases, session processing and anything unattributed), in every region read.
next_cursorNextCursoryes
unavailable_regionsarray of RegionIdyesRegions whose usage could not be read; their streams and other are missing from this answer.

Timestamp

string (date-time)

TurnFailureCode

Why a Turn failed. deadline_exceeded: it ran for the full execution window. start_timeout: it could not start before its start deadline. aborted: it was aborted. agent_retired: its Agent was deleted or retired. account_deleted: its account entered deletion. key_revoked: the key that submitted it was revoked. funding_exhausted: its account could not fund the runtime or inference. usage_store_unavailable: runtime usage could not be recorded, so the runtime was stopped. runtime_retired: its runtime stopped before the Turn ended. runtime_uncertain: the platform could not establish whether the prompt ran, so it was not replayed. pi_failed: the agent itself failed the Turn. legacy_operation_ended: a Turn created before admission records whose operation ended without it; only during the migration.

one of "deadline_exceeded", "start_timeout", "aborted", "agent_retired", "account_deleted", "key_revoked", "funding_exhausted", "usage_store_unavailable", "runtime_retired", "runtime_uncertain", "pi_failed" or "legacy_operation_ended"

UpdateDatabaseRequest

FieldTypeRequiredDescription
public_readbooleanyes

UpdateMemberRequest

At least one field.

FieldTypeRequiredDescription
roleAccountRoleno
nameAgentNamenoThe Agent's new name; refused for a person.

UpdateStreamRequest

FieldTypeRequiredDescription
public_readbooleanyes

Usage

FieldTypeRequiredDescription
periodstringyes
currency"usd"yes
total_microsintegeryes
linesarray of objectyes
lines[].productstringyes
lines[].regionstringyes
lines[].dimensionstringyes
lines[].modelstring or nullyes
lines[].quantitystringyesDecimal quantity, as text.
lines[].amount_microsintegeryes

Uuid

string (uuid)

ValidationIssue

FieldTypeRequiredDescription
fieldstringyes
codestringyes