> Fetch the index of every Tonbo docs page as Markdown at https://tonbo.dev/llms.txt

# Management API: Databases

Last updated 2 November 2026

The account's databases, each one stream at its url; Control creates and deletes them. [The overview](https://tonbo.dev/docs/reference/management.md) covers what every operation shares: authentication, roles, errors, pagination, idempotency and If-Match.

- [`GET /databases`](#list-databases) List the account's databases
- [`POST /databases`](#create-database) Create a database
- [`GET /databases/{name}`](#get-database) Read a database
- [`PATCH /databases/{name}`](#update-database) Publish a database for anonymous reads, or take it private
- [`DELETE /databases/{name}`](#delete-database) Delete a database and everything in it
- [`GET /databases/{name}/usage`](#get-database-activity) Read a database's writes and size over time
- [Objects](#objects): [ActivityPoint](#schema-activity-point), [Database](#schema-database) and [ResourceActivity](#schema-resource-activity)

## List the account's databases

`GET /v1/databases` · CLI: `tonbo database list`

Needs [`account.read`](https://tonbo.dev/docs/reference/management.md#capability-account-read), which `owner`, `admin` and `member` hold. A narrowed key may call it.

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 (and, with `include=daily_writes`, those whose daily writes could not be read).

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `prefix` | query | string | no | Only databases whose name starts with this prefix. 1 to 85 characters. Matches `^(?!m\.)(?!vol\.)(?!s\.)(?!session\.)(?!proc\.)[a-z0-9][a-z0-9._-]{0,84}$`. |
| `include` | query | one of `"daily_writes"` | no | `daily_writes` adds each database's `daily_write_units`, its write units of the last 7 UTC days, from one more read of each region's meter. |
| `cursor` | query | string | no | Opaque cursor tied to the original query shape. At least 1 characters. |
| `limit` | query | integer | no | The most items one page holds, 1 to 200; 50 when omitted. A page may hold fewer; read on with `next_cursor` until it is null. Default `50`. 1 to 200. |

### Response

| Status | Answer |
| --- | --- |
| `200` | Databases |

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | array of [Database](https://tonbo.dev/docs/reference/management/databases.md#schema-database) | yes |  |
| `next_cursor` | string or null | yes | Opaque cursor of the next page; null on the last page. |
| `unavailable_regions` | array of string | yes |  |

| Header | Status | Description |
| --- | --- | --- |
| `WWW-Authenticate` | 401 | Authentication scheme required by this API. |
| `Retry-After` | 429 | Seconds before the client should retry. |

**Example 200 body**

```json
{
  "data": [
    {
      "name": "<name>",
      "kind": "database",
      "owner_id": "3f1c2a9e-5b7d-4c8e-9f10-2a3b4c5d6e7f",
      "region": "<region>",
      "url": "https://example.com",
      "expires_at": "2026-11-02T09:30:00.000Z",
      "incarnation": "<incarnation>",
      "state": "creating",
      "public_read": false,
      "created_at": "2026-11-02T09:30:00.000Z",
      "created_by": "agent.3f1c2a9e-5b7d-4c8e-9f10-2a3b4c5d6e7f",
      "last_write_at": "2026-11-02T09:30:00.000Z",
      "stored_bytes": {
        "log": 0,
        "snapshot": 0
      },
      "measured_at": "2026-11-02T09:30:00.000Z",
      "writes": "accepting",
      "daily_write_units": [
        0
      ]
    }
  ],
  "next_cursor": "<next_cursor>",
  "unavailable_regions": [
    "<unavailable_regions>"
  ]
}
```

### Errors

| Status | Code | When | Send again | What to do |
| --- | --- | --- | --- | --- |
| `401` | [`exchange_required`](https://tonbo.dev/docs/reference/management.md#exchange_required) | The key acts as an Agent, and an Agent’s key is never sent itself. | No | Exchange the key for a token at the endpoint the answer’s `token_endpoint` names, and send the token as `Authorization: Bearer`. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#exchange_required)) |
| `401` | [`unauthenticated`](https://tonbo.dev/docs/reference/management.md#unauthenticated) | The request carries no key or token, or one that is invalid, expired or revoked. | No | Send a valid key, or a token that has not expired, as `Authorization: Bearer`. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#unauthenticated)) |
| `403` | [`forbidden`](https://tonbo.dev/docs/reference/management.md#forbidden) | The key’s role does not hold the capability the operation needs, the key is narrowed away from the resource, or the account already holds as many of the resource as it may. | No | Use a key whose role holds the capability, or one that is not narrowed; delete what you no longer need, or ask for a higher limit. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#forbidden)) |
| `422` | [`validation_error`](https://tonbo.dev/docs/reference/management.md#validation_error) | A parameter, header or body field breaks its rule; `errors` names each field. | No | Correct the fields `errors` names; the operation’s reference gives each one’s rule. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#validation_error)) |
| `429` | [`rate_limited`](https://tonbo.dev/docs/reference/management.md#rate_limited) | The key or the account sent more requests than its rate allows. | After Retry-After | Wait for the seconds Retry-After names, then send the request again; spread requests out if it recurs. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#rate_limited)) |

### Retries

- A read: sending it again changes nothing.
- `429` carry `Retry-After`: wait that many seconds, then send the same request again.

**Request**

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

## Create a database

`POST /v1/databases` · CLI: `tonbo database create`

Needs [`databases.write`](https://tonbo.dev/docs/reference/management.md#capability-databases-write), which `owner`, `admin` and `member` hold. A narrowed key may not.

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

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | string | yes | A value you choose for this one creation, 1 to 255 characters, such as a new UUID. Send a new one for each resource you create, and the same one only to retry that request after a timeout or a lost answer: the retry answers what the first request created, in its current state, and never creates a second. A key reused for a different database, stream or Machine answers 409. 1 to 255 characters. |

### Request body

`application/json`, required. Unknown fields are refused.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes | The new database's name. 409 when a live database of the account in any region has it; a deleted database's name is free again. 1 to 85 characters. Matches `^(?!m\.)(?!vol\.)(?!s\.)(?!session\.)(?!proc\.)(?!(?:streams\|new)$)(?!.*\.\.)[a-z0-9](?:[a-z0-9._-]{0,83}[a-z0-9])?$`. |
| `region` | string | no | The database's region, fixed for its life; the region whose `default` is true when omitted. A region closed to databases answers 503. Matches `^[a-z]{2}-[a-z]+-[0-9]$`. |
| `expires_at` | string (date-time) | no | When 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. |

### Response

| Status | Answer |
| --- | --- |
| `201` | The database, active |
| `202` | The database, still being created |

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | [Database](https://tonbo.dev/docs/reference/management/databases.md#schema-database) | yes |  |

| Header | Status | Description |
| --- | --- | --- |
| `WWW-Authenticate` | 401 | Authentication scheme required by this API. |
| `Retry-After` | 429, 503 | Seconds before the client should retry. |

**Example 201 body**

```json
{
  "data": {
    "name": "<name>",
    "kind": "database",
    "owner_id": "3f1c2a9e-5b7d-4c8e-9f10-2a3b4c5d6e7f",
    "region": "<region>",
    "url": "https://example.com",
    "expires_at": "2026-11-02T09:30:00.000Z",
    "incarnation": "<incarnation>",
    "state": "creating",
    "public_read": false,
    "created_at": "2026-11-02T09:30:00.000Z",
    "created_by": "agent.3f1c2a9e-5b7d-4c8e-9f10-2a3b4c5d6e7f",
    "last_write_at": "2026-11-02T09:30:00.000Z",
    "stored_bytes": {
      "log": 0,
      "snapshot": 0
    },
    "measured_at": "2026-11-02T09:30:00.000Z",
    "writes": "accepting",
    "daily_write_units": [
      0
    ]
  }
}
```

### Errors

| Status | Code | When | Send again | What to do |
| --- | --- | --- | --- | --- |
| `401` | [`exchange_required`](https://tonbo.dev/docs/reference/management.md#exchange_required) | The key acts as an Agent, and an Agent’s key is never sent itself. | No | Exchange the key for a token at the endpoint the answer’s `token_endpoint` names, and send the token as `Authorization: Bearer`. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#exchange_required)) |
| `401` | [`unauthenticated`](https://tonbo.dev/docs/reference/management.md#unauthenticated) | The request carries no key or token, or one that is invalid, expired or revoked. | No | Send a valid key, or a token that has not expired, as `Authorization: Bearer`. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#unauthenticated)) |
| `402` | [`payment_required`](https://tonbo.dev/docs/reference/management.md#payment_required) | The account’s balance is zero, so nothing new is created. | No | Add credit on Usage & billing, then send the request again. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#payment_required)) |
| `403` | [`forbidden`](https://tonbo.dev/docs/reference/management.md#forbidden) | The key’s role does not hold the capability the operation needs, the key is narrowed away from the resource, or the account already holds as many of the resource as it may. | No | Use a key whose role holds the capability, or one that is not narrowed; delete what you no longer need, or ask for a higher limit. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#forbidden)) |
| `409` | [`idempotency_conflict`](https://tonbo.dev/docs/reference/management.md#idempotency_conflict) | The Idempotency-Key was already sent with a different request. | No | Send the request with a new Idempotency-Key, or send the request the key was first used for. ([more](https://tonbo.dev/docs/machines/troubleshooting/manage.md#idempotency_conflict)) |
| `409` | [`conflict`](https://tonbo.dev/docs/reference/management.md#conflict) | The name is held by another live resource of the account, the Idempotency-Key already created another resource or one since deleted, or the resource’s current state does not allow the change. `detail` says which, and `errors` names the field. | No | Pick another name or a new Idempotency-Key, or read the resource again and act on its current state. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#conflict)) |
| `422` | [`validation_error`](https://tonbo.dev/docs/reference/management.md#validation_error) | A parameter, header or body field breaks its rule; `errors` names each field. | No | Correct the fields `errors` names; the operation’s reference gives each one’s rule. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#validation_error)) |
| `429` | [`rate_limited`](https://tonbo.dev/docs/reference/management.md#rate_limited) | The key or the account sent more requests than its rate allows. | After Retry-After | Wait for the seconds Retry-After names, then send the request again; spread requests out if it recurs. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#rate_limited)) |
| `503` | [`region_unavailable`](https://tonbo.dev/docs/reference/management.md#region_unavailable) | The region could not be reached or read, or it takes no new resource of this kind. Every answer carries Retry-After. | After Retry-After | Send the request again after Retry-After; if `detail` says the region takes no new resource of this kind, name another region. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#region_unavailable)) |

### Retries

- Send a new [`Idempotency-Key`](#create-database-header-idempotency-key) with each request and the same one when you send it again: a repeat answers what the first request did, and the key reused for a different request answers 409 (see [Idempotency](https://tonbo.dev/docs/reference/management.md#idempotency)).
- `429` and `503` carry `Retry-After`: wait that many seconds, then send the same request again.

**Request**

```shell
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":"notes"}'
```

## Read a database

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

Needs [`account.read`](https://tonbo.dev/docs/reference/management.md#capability-account-read), which `owner`, `admin` and `member` hold. A narrowed key may call it.

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

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `name` | path | string or string | yes | The database's name, or a Machine's database as `m.<machine id>`. |

### Response

| Status | Answer |
| --- | --- |
| `200` | The database |

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | [Database](https://tonbo.dev/docs/reference/management/databases.md#schema-database) | yes |  |

| Header | Status | Description |
| --- | --- | --- |
| `ETag` | 200 | Current resource version for optional conditional updates. |
| `WWW-Authenticate` | 401 | Authentication scheme required by this API. |
| `Retry-After` | 429 | Seconds before the client should retry. |

**Example 200 body**

```json
{
  "data": {
    "name": "<name>",
    "kind": "database",
    "owner_id": "3f1c2a9e-5b7d-4c8e-9f10-2a3b4c5d6e7f",
    "region": "<region>",
    "url": "https://example.com",
    "expires_at": "2026-11-02T09:30:00.000Z",
    "incarnation": "<incarnation>",
    "state": "creating",
    "public_read": false,
    "created_at": "2026-11-02T09:30:00.000Z",
    "created_by": "agent.3f1c2a9e-5b7d-4c8e-9f10-2a3b4c5d6e7f",
    "last_write_at": "2026-11-02T09:30:00.000Z",
    "stored_bytes": {
      "log": 0,
      "snapshot": 0
    },
    "measured_at": "2026-11-02T09:30:00.000Z",
    "writes": "accepting",
    "daily_write_units": [
      0
    ]
  }
}
```

### Errors

| Status | Code | When | Send again | What to do |
| --- | --- | --- | --- | --- |
| `401` | [`exchange_required`](https://tonbo.dev/docs/reference/management.md#exchange_required) | The key acts as an Agent, and an Agent’s key is never sent itself. | No | Exchange the key for a token at the endpoint the answer’s `token_endpoint` names, and send the token as `Authorization: Bearer`. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#exchange_required)) |
| `401` | [`unauthenticated`](https://tonbo.dev/docs/reference/management.md#unauthenticated) | The request carries no key or token, or one that is invalid, expired or revoked. | No | Send a valid key, or a token that has not expired, as `Authorization: Bearer`. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#unauthenticated)) |
| `403` | [`forbidden`](https://tonbo.dev/docs/reference/management.md#forbidden) | The key’s role does not hold the capability the operation needs, the key is narrowed away from the resource, or the account already holds as many of the resource as it may. | No | Use a key whose role holds the capability, or one that is not narrowed; delete what you no longer need, or ask for a higher limit. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#forbidden)) |
| `404` | [`not_found`](https://tonbo.dev/docs/reference/management.md#not_found) | No live resource of the key’s account has that name or id, or the key is narrowed away from it. | No | Check the name, and that the key belongs to the account that holds it; list the resources to see what exists. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#not_found)) |
| `429` | [`rate_limited`](https://tonbo.dev/docs/reference/management.md#rate_limited) | The key or the account sent more requests than its rate allows. | After Retry-After | Wait for the seconds Retry-After names, then send the request again; spread requests out if it recurs. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#rate_limited)) |

### Retries

- A read: sending it again changes nothing.
- `429` carry `Retry-After`: wait that many seconds, then send the same request again.

**Request**

```shell
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`

Needs [`databases.publish`](https://tonbo.dev/docs/reference/management.md#capability-databases-publish), which `owner` and `admin` hold. A narrowed key may not.

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

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `name` | path | string or string | yes | The database's name, or a Machine's database as `m.<machine id>`. |
| `If-Match` | header | string | no | Optional conditional update; omitted requests use last-write-wins. At most 500 characters. |

### Request body

`application/json`, required. Unknown fields are refused.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `public_read` | boolean | yes | `true` publishes the database: anyone with its URL reads it without a key, and writes still need one. `false` makes it private again. |

### Response

| Status | Answer |
| --- | --- |
| `200` | The database |

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | [Database](https://tonbo.dev/docs/reference/management/databases.md#schema-database) | yes |  |

| Header | Status | Description |
| --- | --- | --- |
| `ETag` | 200 | Current resource version for optional conditional updates. |
| `WWW-Authenticate` | 401 | Authentication scheme required by this API. |
| `Retry-After` | 429 | Seconds before the client should retry. |

**Example 200 body**

```json
{
  "data": {
    "name": "<name>",
    "kind": "database",
    "owner_id": "3f1c2a9e-5b7d-4c8e-9f10-2a3b4c5d6e7f",
    "region": "<region>",
    "url": "https://example.com",
    "expires_at": "2026-11-02T09:30:00.000Z",
    "incarnation": "<incarnation>",
    "state": "creating",
    "public_read": false,
    "created_at": "2026-11-02T09:30:00.000Z",
    "created_by": "agent.3f1c2a9e-5b7d-4c8e-9f10-2a3b4c5d6e7f",
    "last_write_at": "2026-11-02T09:30:00.000Z",
    "stored_bytes": {
      "log": 0,
      "snapshot": 0
    },
    "measured_at": "2026-11-02T09:30:00.000Z",
    "writes": "accepting",
    "daily_write_units": [
      0
    ]
  }
}
```

### Errors

| Status | Code | When | Send again | What to do |
| --- | --- | --- | --- | --- |
| `401` | [`exchange_required`](https://tonbo.dev/docs/reference/management.md#exchange_required) | The key acts as an Agent, and an Agent’s key is never sent itself. | No | Exchange the key for a token at the endpoint the answer’s `token_endpoint` names, and send the token as `Authorization: Bearer`. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#exchange_required)) |
| `401` | [`unauthenticated`](https://tonbo.dev/docs/reference/management.md#unauthenticated) | The request carries no key or token, or one that is invalid, expired or revoked. | No | Send a valid key, or a token that has not expired, as `Authorization: Bearer`. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#unauthenticated)) |
| `403` | [`forbidden`](https://tonbo.dev/docs/reference/management.md#forbidden) | The key’s role does not hold the capability the operation needs, the key is narrowed away from the resource, or the account already holds as many of the resource as it may. | No | Use a key whose role holds the capability, or one that is not narrowed; delete what you no longer need, or ask for a higher limit. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#forbidden)) |
| `404` | [`not_found`](https://tonbo.dev/docs/reference/management.md#not_found) | No live resource of the key’s account has that name or id, or the key is narrowed away from it. | No | Check the name, and that the key belongs to the account that holds it; list the resources to see what exists. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#not_found)) |
| `409` | [`managed_by_machine`](https://tonbo.dev/docs/reference/management.md#managed_by_machine) | The database belongs to a Machine: it is deleted with its Machine, and it is never deleted or made public or private by itself. | No | Delete the Machine to delete its database. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#managed_by_machine)) |
| `409` | [`conflict`](https://tonbo.dev/docs/reference/management.md#conflict) | The name is held by another live resource of the account, the Idempotency-Key already created another resource or one since deleted, or the resource’s current state does not allow the change. `detail` says which, and `errors` names the field. | No | Pick another name or a new Idempotency-Key, or read the resource again and act on its current state. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#conflict)) |
| `412` | [`precondition_failed`](https://tonbo.dev/docs/reference/management.md#precondition_failed) | If-Match does not name the resource’s current ETag: it changed since you read it. Nothing changed. | No | Read the resource again, check that the change still applies, and send it with the new ETag. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#precondition_failed)) |
| `422` | [`validation_error`](https://tonbo.dev/docs/reference/management.md#validation_error) | A parameter, header or body field breaks its rule; `errors` names each field. | No | Correct the fields `errors` names; the operation’s reference gives each one’s rule. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#validation_error)) |
| `429` | [`rate_limited`](https://tonbo.dev/docs/reference/management.md#rate_limited) | The key or the account sent more requests than its rate allows. | After Retry-After | Wait for the seconds Retry-After names, then send the request again; spread requests out if it recurs. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#rate_limited)) |

### Retries

- It takes no Idempotency-Key; its description above says what sending it again does.
- [`If-Match`](#update-database-header-if-match) is optional: send the `ETag` you read, and a 412 `precondition_failed` means the resource changed since; read it again and decide (see [If-Match](https://tonbo.dev/docs/reference/management.md#if-match)).
- `429` carry `Retry-After`: wait that many seconds, then send the same request again.

**Request**

```shell
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`

Needs [`databases.write`](https://tonbo.dev/docs/reference/management.md#capability-databases-write), which `owner`, `admin` and `member` hold. A narrowed key may not.

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

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `name` | path | string or string | yes | The database's name, or a Machine's database as `m.<machine id>`. |

### Response

| Status | Answer |
| --- | --- |
| `200` | The database, deleted |
| `202` | The database, being deleted |

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | [Database](https://tonbo.dev/docs/reference/management/databases.md#schema-database) | yes |  |

| Header | Status | Description |
| --- | --- | --- |
| `WWW-Authenticate` | 401 | Authentication scheme required by this API. |
| `Retry-After` | 429 | Seconds before the client should retry. |

**Example 200 body**

```json
{
  "data": {
    "name": "<name>",
    "kind": "database",
    "owner_id": "3f1c2a9e-5b7d-4c8e-9f10-2a3b4c5d6e7f",
    "region": "<region>",
    "url": "https://example.com",
    "expires_at": "2026-11-02T09:30:00.000Z",
    "incarnation": "<incarnation>",
    "state": "creating",
    "public_read": false,
    "created_at": "2026-11-02T09:30:00.000Z",
    "created_by": "agent.3f1c2a9e-5b7d-4c8e-9f10-2a3b4c5d6e7f",
    "last_write_at": "2026-11-02T09:30:00.000Z",
    "stored_bytes": {
      "log": 0,
      "snapshot": 0
    },
    "measured_at": "2026-11-02T09:30:00.000Z",
    "writes": "accepting",
    "daily_write_units": [
      0
    ]
  }
}
```

### Errors

| Status | Code | When | Send again | What to do |
| --- | --- | --- | --- | --- |
| `401` | [`exchange_required`](https://tonbo.dev/docs/reference/management.md#exchange_required) | The key acts as an Agent, and an Agent’s key is never sent itself. | No | Exchange the key for a token at the endpoint the answer’s `token_endpoint` names, and send the token as `Authorization: Bearer`. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#exchange_required)) |
| `401` | [`unauthenticated`](https://tonbo.dev/docs/reference/management.md#unauthenticated) | The request carries no key or token, or one that is invalid, expired or revoked. | No | Send a valid key, or a token that has not expired, as `Authorization: Bearer`. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#unauthenticated)) |
| `403` | [`forbidden`](https://tonbo.dev/docs/reference/management.md#forbidden) | The key’s role does not hold the capability the operation needs, the key is narrowed away from the resource, or the account already holds as many of the resource as it may. | No | Use a key whose role holds the capability, or one that is not narrowed; delete what you no longer need, or ask for a higher limit. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#forbidden)) |
| `404` | [`not_found`](https://tonbo.dev/docs/reference/management.md#not_found) | No live resource of the key’s account has that name or id, or the key is narrowed away from it. | No | Check the name, and that the key belongs to the account that holds it; list the resources to see what exists. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#not_found)) |
| `429` | [`rate_limited`](https://tonbo.dev/docs/reference/management.md#rate_limited) | The key or the account sent more requests than its rate allows. | After Retry-After | Wait for the seconds Retry-After names, then send the request again; spread requests out if it recurs. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#rate_limited)) |

### Retries

- It takes no Idempotency-Key; its description above says what sending it again does.
- `429` carry `Retry-After`: wait that many seconds, then send the same request again.

**Request**

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

## Read a database's writes and size over time

`GET /v1/databases/{name}/usage` · CLI: `tonbo database activity`

Needs [`account.read`](https://tonbo.dev/docs/reference/management.md#capability-account-read), which `owner`, `admin` and `member` hold. A narrowed key may call it.

A live database's writes per hour or day (UTC) over a window of at most 31 days, by default the last 24 hours hourly or the last 30 days daily, through the current step, its successful reads in each step, and its size at the end of each step (now, for the current one), from its region's meter, with the meter's checkpoint as `as_of` and the database's last write. A Machine's database counts its Machine's logs and inbox streams in its writes and reads, as its usage does; its size is the database's own. Only the database as it is now counts, not an earlier database of the same name. A write counts in the step that holds its settlement hour, so the current step grows until it ends; a step that starts after now answers null. A key narrowed to databases reads the databases it reaches, and any other name answers 404 with the same status and body as a database that does not exist. 503 when the database's region cannot be read.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `name` | path | string or string | yes | The database's name, or a Machine's database as `m.<machine id>`. |
| `granularity` | query | one of `"hour"` or `"day"` | no | One point per UTC hour (the default) or per UTC day. Default `"hour"`. |
| `from` | query | string (date-time) | no | The window's start, on a UTC hour (or day, for daily points); by default 24 hours (or 30 days) before `to`. |
| `to` | query | string (date-time) | no | The window's end, exclusive, on a UTC hour (or day), at most 31 days after `from`; by default the end of the current hour (or day). |

### Response

| Status | Answer |
| --- | --- |
| `200` | The database's activity |

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | [ResourceActivity](https://tonbo.dev/docs/reference/management/databases.md#schema-resource-activity) | yes | A database's or stream's writes, reads and size over a window, from its region's meter. |

| Header | Status | Description |
| --- | --- | --- |
| `WWW-Authenticate` | 401 | Authentication scheme required by this API. |
| `Retry-After` | 429, 503 | Seconds before the client should retry. |

**Example 200 body**

```json
{
  "data": {
    "name": "<name>",
    "region": "<region>",
    "granularity": "hour",
    "from": "2026-11-02T09:30:00.000Z",
    "to": "2026-11-02T09:30:00.000Z",
    "as_of": "2026-11-02T09:30:00.000Z",
    "last_write_at": "2026-11-02T09:30:00.000Z",
    "points": [
      {
        "start": "2026-11-02T09:30:00.000Z",
        "write_units": 0,
        "append_bytes": 0,
        "records": 0,
        "log_bytes": 0,
        "snapshot_bytes": 0,
        "reads": 0,
        "read_bytes": 0,
        "live_reads": 0,
        "live_read_bytes": 0
      }
    ]
  }
}
```

### Errors

| Status | Code | When | Send again | What to do |
| --- | --- | --- | --- | --- |
| `401` | [`exchange_required`](https://tonbo.dev/docs/reference/management.md#exchange_required) | The key acts as an Agent, and an Agent’s key is never sent itself. | No | Exchange the key for a token at the endpoint the answer’s `token_endpoint` names, and send the token as `Authorization: Bearer`. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#exchange_required)) |
| `401` | [`unauthenticated`](https://tonbo.dev/docs/reference/management.md#unauthenticated) | The request carries no key or token, or one that is invalid, expired or revoked. | No | Send a valid key, or a token that has not expired, as `Authorization: Bearer`. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#unauthenticated)) |
| `403` | [`forbidden`](https://tonbo.dev/docs/reference/management.md#forbidden) | The key’s role does not hold the capability the operation needs, the key is narrowed away from the resource, or the account already holds as many of the resource as it may. | No | Use a key whose role holds the capability, or one that is not narrowed; delete what you no longer need, or ask for a higher limit. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#forbidden)) |
| `404` | [`not_found`](https://tonbo.dev/docs/reference/management.md#not_found) | No live resource of the key’s account has that name or id, or the key is narrowed away from it. | No | Check the name, and that the key belongs to the account that holds it; list the resources to see what exists. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#not_found)) |
| `422` | [`validation_error`](https://tonbo.dev/docs/reference/management.md#validation_error) | A parameter, header or body field breaks its rule; `errors` names each field. | No | Correct the fields `errors` names; the operation’s reference gives each one’s rule. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#validation_error)) |
| `429` | [`rate_limited`](https://tonbo.dev/docs/reference/management.md#rate_limited) | The key or the account sent more requests than its rate allows. | After Retry-After | Wait for the seconds Retry-After names, then send the request again; spread requests out if it recurs. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#rate_limited)) |
| `503` | [`region_unavailable`](https://tonbo.dev/docs/reference/management.md#region_unavailable) | The region could not be reached or read, or it takes no new resource of this kind. Every answer carries Retry-After. | After Retry-After | Send the request again after Retry-After; if `detail` says the region takes no new resource of this kind, name another region. ([more](https://tonbo.dev/docs/databases/troubleshooting/manage.md#region_unavailable)) |

### Retries

- A read: sending it again changes nothing.
- `429` and `503` carry `Retry-After`: wait that many seconds, then send the same request again.

**Request**

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

## Objects

### ActivityPoint

One step of a database's or stream's activity.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string (date-time) | yes | The step's start, on its UTC hour or day. |
| `write_units` | integer or null | yes | Write units committed in the step (contracts/billing/streams-prices-v1.json `write_unit_bytes` each); null for a step that starts after now. At least 0. |
| `append_bytes` | integer or null | yes | Bytes appended in the step; null for a step that starts after now. At least 0. |
| `records` | integer or null | yes | Records appended in the step; null for a step that starts after now. At least 0. |
| `log_bytes` | integer or null | yes | The retained log at the step's end (now, for the current step), as `stored_bytes.log` counts it; null before the meter's first sample and for a step that starts after now. At least 0. |
| `snapshot_bytes` | integer or null | yes | The snapshots at the step's end, as `stored_bytes.snapshot` counts them; null before the meter's first sample and for a step that starts after now. At least 0. |
| `reads` | integer or null | yes | Reads (not live) that its region's gateway answered with success in the step, counted in the step their export window ended in. Reads a cache in front of the gateway answered are not counted, and steps from before the region's gateway counted reads per stream read 0. Null for a step that starts after now. At least 0. |
| `read_bytes` | integer or null | yes | The bytes those reads streamed; null for a step that starts after now. At least 0. |
| `live_reads` | integer or null | yes | Live reads (long-poll or SSE) that its region's gateway answered with success, counted in the step they ended, as `reads` counts them; null for a step that starts after now. At least 0. |
| `live_read_bytes` | integer or null | yes | The bytes those live reads streamed; null for a step that starts after now. At least 0. |

**Example ActivityPoint**

```json
{
  "start": "2026-11-02T09:30:00.000Z",
  "write_units": 0,
  "append_bytes": 0,
  "records": 0,
  "log_bytes": 0,
  "snapshot_bytes": 0,
  "reads": 0,
  "read_bytes": 0,
  "live_reads": 0,
  "live_read_bytes": 0
}
```

### Database

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string or string | yes | The database's name; a Machine's database is `m.<machine id>`. |
| `kind` | one of `"database"` or `"machine"` | yes | `database` for one a member created, `machine` for a Machine's own, which its Machine creates and deletes. |
| `owner_id` | string (uuid) or null | yes | The Machine that owns a `machine` row; null for a member's database. |
| `region` | string | yes | The region the database lives in, fixed for its life. Matches `^[a-z]{2}-[a-z]+-[0-9]$`. |
| `url` | string (uri) | yes | The database's data-plane address, `{region's database origin}/{account id}/{name}`; open it with the SQLite on Ursula client. |
| `expires_at` | string (date-time) or null | yes | When 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. |
| `incarnation` | string or null | yes | Ursula's Stream-Incarnation as a decimal; null while creating. A client that cached the URL resolves the database again when it changes. Matches `^(0\|[1-9][0-9]{0,19})$`. |
| `state` | one of `"creating"`, `"active"`, `"deleting"` or `"deleted"` | yes | `deleted` appears only in the answer to the deletion that completed it. |
| `public_read` | boolean | yes | Whether anonymous reads are served. |
| `created_at` | string (date-time) | yes | When the database was created. |
| `created_by` | string | yes | The member who created it, or for a Machine's database the member who created the Machine. Matches `^[a-z0-9._-]{1,64}$`. |
| `last_write_at` | string (date-time) or null | yes | The 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_bytes` | object | yes | What the region's meter last saw stored; null when not yet reported or unavailable. |
| `stored_bytes.log` | integer or null | yes | Bytes of the database's retained log, as Tonbo stores it; null when not reported. At least 0. |
| `stored_bytes.snapshot` | integer or null | yes | Bytes of its snapshots; null when not reported. At least 0. |
| `measured_at` | string (date-time) or null | no | When 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. |
| `writes` | one of `"accepting"`, `"suspended"` or `null` | no | Whether 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. |
| `daily_write_units` | array of integer or null | no | The database's write units on each of the last 7 UTC days, oldest first, the last being today so far, for a sparkline: only in a list asked for it with `include=daily_writes`, never in a show; null when its region could not be read. Only the database as it is now counts, not an earlier database of the same name. 7 to 7 items. |

**Example Database**

```json
{
  "name": "<name>",
  "kind": "database",
  "owner_id": "3f1c2a9e-5b7d-4c8e-9f10-2a3b4c5d6e7f",
  "region": "<region>",
  "url": "https://example.com",
  "expires_at": "2026-11-02T09:30:00.000Z",
  "incarnation": "<incarnation>",
  "state": "creating",
  "public_read": false,
  "created_at": "2026-11-02T09:30:00.000Z",
  "created_by": "agent.3f1c2a9e-5b7d-4c8e-9f10-2a3b4c5d6e7f",
  "last_write_at": "2026-11-02T09:30:00.000Z",
  "stored_bytes": {
    "log": 0,
    "snapshot": 0
  },
  "measured_at": "2026-11-02T09:30:00.000Z",
  "writes": "accepting",
  "daily_write_units": [
    0
  ]
}
```

### ResourceActivity

A database's or stream's writes, reads and size over a window, from its region's meter.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes | The database's or stream's name. |
| `region` | string | yes | Its region, whose meter answered. Matches `^[a-z]{2}-[a-z]+-[0-9]$`. |
| `granularity` | one of `"hour"` or `"day"` | yes | One point per UTC hour or per UTC day. |
| `from` | string (date-time) | yes | The window's start. |
| `to` | string (date-time) | yes | The window's end, exclusive. |
| `as_of` | string (date-time) or null | yes | When the region's meter last completed a poll, about once a minute while it is healthy; writes and sizes after it are not counted yet. Null when it has never completed one. |
| `last_write_at` | string (date-time) or null | yes | The last write the region's meter saw, as the show answers it; null before the first write. |
| `points` | array of [ActivityPoint](https://tonbo.dev/docs/reference/management/databases.md#schema-activity-point) | yes | One point per step of the window, oldest first. |

**Example ResourceActivity**

```json
{
  "name": "<name>",
  "region": "<region>",
  "granularity": "hour",
  "from": "2026-11-02T09:30:00.000Z",
  "to": "2026-11-02T09:30:00.000Z",
  "as_of": "2026-11-02T09:30:00.000Z",
  "last_write_at": "2026-11-02T09:30:00.000Z",
  "points": [
    {
      "start": "2026-11-02T09:30:00.000Z",
      "write_units": 0,
      "append_bytes": 0,
      "records": 0,
      "log_bytes": 0,
      "snapshot_bytes": 0,
      "reads": 0,
      "read_bytes": 0,
      "live_reads": 0,
      "live_read_bytes": 0
    }
  ]
}
```
