REST API Reference
The DB9 REST API lets you manage databases, execute SQL, and interact with the platform programmatically. All customer endpoints live under https://api.db9.ai/customer.
For most use cases, the CLI or TypeScript SDK provide a more ergonomic interface. The REST API is ideal for custom integrations, CI/CD pipelines, or languages without an SDK.
Authentication
Section titled “Authentication”Most requests require a Bearer token in the Authorization header:
curl -H "Authorization: Bearer $DB9_API_KEY" https://api.db9.ai/customer/databasesExceptions: POST /customer/anonymous-register requires no credentials. POST /customer/anonymous-refresh authenticates using anonymous_id + anonymous_secret in the request body (no Bearer token).
Obtain tokens via:
db9 token create(CLI)db9 login --api-key <KEY>(verifies and saves an existing API key locally)- Anonymous bootstrap (
/customer/anonymous-register)
Error Responses
Section titled “Error Responses”Errors handled by the API normally use the following JSON shape. Request decoding errors can
return plain text, and SQL execution errors use a different response body with HTTP 200; see
Error codes before handling every response as JSON.
{ "message": "human-readable message describing what went wrong"}| Status | Meaning |
|---|---|
400 | Bad request — missing or invalid fields |
401 | Unauthorized — missing or invalid token |
403 | Forbidden — token lacks required scope |
404 | Not found — resource does not exist |
409 | Conflict — resource already exists |
422 | Unprocessable — valid JSON but semantic error |
429 | Rate limited — slow down and retry |
500 | Internal server error |
Account & Sessions
Section titled “Account & Sessions”/customer/me Return the current customer's identity and profile.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/me{ "id": "cus_01h9xxxxxxxxxxxxxxxx", "email": "user@example.com", "created_at": "2026-01-15T10:00:00Z", "status": "active"}/customer/anonymous-register Create an anonymous account and return a session token. No credentials required.
curl -X POST https://api.db9.ai/customer/anonymous-register{ "anonymous_id": "anon_01h9xxxxxxxxxxxxxxxx", "anonymous_secret": "sk_anon_xxxxxxxxxxxxxxxxxxxx", "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "expires_at": "2027-01-15T10:00:00Z", "is_anonymous": true}/customer/anonymous-refresh Refresh an anonymous session token using the customer ID and secret.
| Field | Type | Required | Description |
|---|---|---|---|
anonymous_id | string | yes | Anonymous customer ID |
anonymous_secret | string | yes | Anonymous account secret |
curl -X POST \ -H "Content-Type: application/json" \ -d '{"anonymous_id": "anon_01h9xx", "anonymous_secret": "sk_anon_xx"}' \ https://api.db9.ai/customer/anonymous-refresh{ "anonymous_id": "anon_01h9xxxxxxxxxxxxxxxx", "anonymous_secret": "sk_anon_xxxxxxxxxxxxxxxxxxxx" }{ "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "expires_at": "2027-02-15T10:00:00Z"}/customer/claim Upgrade an anonymous account to a verified identity using an Auth0 id_token. Requires a Bearer token.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"id_token": "<Auth0 id_token>"}' \ https://api.db9.ai/customer/claim{ "id_token": "<Auth0 id_token>" }{ "id": "cus_01h9xxxxxxxxxxxxxxxx", "email": "user@example.com", "claimed": true}/customer/adopt-anonymous-databases/preflight Pre-check eligibility and quotas before adopting databases from an anonymous account.
| Field | Type | Required | Description |
|---|---|---|---|
anonymous_customer_id | string | yes | Customer ID of the anonymous account |
anonymous_secret | string | yes | Secret for the anonymous account |
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"anonymous_customer_id": "anon_01h9xx", "anonymous_secret": "sk_anon_xx"}' \ https://api.db9.ai/customer/adopt-anonymous-databases/preflight{ "anonymous_customer_id": "anon_01h9xxxxxxxxxxxxxxxx", "anonymous_secret": "sk_anon_xxxxxxxxxxxxxxxxxxxx"}{ "databases": [ { "id": "db_01h9xxxxxxxxxxxxxxxx", "name": "myapp", "state": "ACTIVE" } ], "quota_ok": true, "current_count": 1, "database_limit": 10}/customer/adopt-anonymous-databases Transfer databases from an anonymous account to your authenticated account.
| Field | Type | Required | Description |
|---|---|---|---|
anonymous_customer_id | string | yes | Customer ID of the anonymous account |
anonymous_secret | string | yes | Secret for the anonymous account |
database_ids | string[] | yes | List of database IDs to adopt (max 100) |
idempotency_key | string | yes | Unique key for idempotent retries |
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"anonymous_customer_id": "anon_01h9xx", "anonymous_secret": "sk_anon_xx", "database_ids": ["db_01h9xx"], "idempotency_key": "adopt-2026-01-15"}' \ https://api.db9.ai/customer/adopt-anonymous-databases{ "anonymous_customer_id": "anon_01h9xxxxxxxxxxxxxxxx", "anonymous_secret": "sk_anon_xxxxxxxxxxxxxxxxxxxx", "database_ids": ["db_01h9xxxxxxxxxxxxxxxx"], "idempotency_key": "adopt-2026-01-15"}{ "transferred": ["db_01h9xxxxxxxxxxxxxxxx"], "skipped": [], "total_transferred": 1}Databases
Section titled “Databases”A direct PostgreSQL username is <tenant_id>.<role>; DB9 uses the prefix as the tenant routing identifier. The API’s database_id identifies the database resource, while admin_user is only the role name (admin). Use the complete username in connection_string for direct connections rather than constructing it from those fields.
/customer/databases Create a new database. The response includes the admin password; its connection string does not.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Database name |
region | string | no | Deployment region |
admin_password | string | no | Admin password (auto-generated if omitted) |
project_id | string | no | Project ID to group databases under |
connection_string carries no password and includes the tenant-qualified username. Add
admin_password to it yourself, or use a short-lived token from
connect-token.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "myapp", "region": "us-east-1"}' \ https://api.db9.ai/customer/databases{ "name": "myapp", "region": "us-east-1"}{ "id": "ykfpouzs1hv9", "name": "myapp", "state": "ACTIVE", "project_id": "6bd5903d-...", "region": "us-east-1", "admin_user": "admin", "admin_password": "generated-password", "created_at": "2026-10-26T15:49:24.809025405+00:00", "last_active_at": null, "last_modified_at": null, "connection_string": "postgresql://ykfpouzs1hv9.admin@pg.d0000.db9.io:5432/postgres", "fs_websocket_url": "wss://ws.d0000.db9.io:5480"}/customer/databases List all databases in your account.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases[ { "created_at": "2026-10-26T15:49:24.809025405+00:00", "id": "ykfpouzs1hv9", "last_active_at": null, "last_modified_at": null, "name": "myapp", "project_id": "6bd5903d-...", "region": "us-east-1", "state": "ACTIVE" }]/customer/databases/{database_id} Get database details including connection string and current state.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx{ "id": "ykfpouzs1hv9", "name": "myapp", "state": "ACTIVE", "project_id": "6bd5903d-...", "region": "us-east-1", "endpoints": [ { "host": "pg.d0000.db9.io", "port": 5432, "type": "primary", "priority": 100, "description": "db9-server primary endpoint", "enabled": true } ], "admin_user": "admin", "created_at": "2026-10-26T15:49:24.809025405+00:00", "last_active_at": null, "last_modified_at": null, "connection_string": "postgresql://ykfpouzs1hv9.admin@pg.d0000.db9.io:5432/postgres", "fs_websocket_url": "wss://ws.d0000.db9.io:5480"}/customer/databases/{database_id} Permanently delete a database. This action is irreversible.
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx{ "message": "Database disabled" }/customer/databases/{database_id}/reset-password Reset the admin password for a database and return the new credentials.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/reset-password{ "admin_user": "admin", "admin_password": "new-generated-password", "connection_string": "postgresql://ykfpouzs1hv9.admin@pg.d0000.db9.io:5432/postgres", "fs_websocket_url": "wss://ws.d0000.db9.io:5480", "connection_string_with_password": "postgresql://ykfpouzs1hv9.admin:new-generated-password@pg.d0000.db9.io:5432/postgres"}/customer/databases/{database_id}/credentials Retrieve stored admin credentials for a database.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/credentials{ "admin_user": "admin", "admin_password": "stored-password", "connection_string": "postgresql://ykfpouzs1hv9.admin@pg.d0000.db9.io:5432/postgres", "fs_websocket_url": "wss://ws.d0000.db9.io:5480", "connection_string_with_password": "postgresql://ykfpouzs1hv9.admin:stored-password@pg.d0000.db9.io:5432/postgres"}/customer/databases/{database_id}/schema Get the database schema — tables, columns, and types.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/schema{ "tables": [ { "schema": "public", "name": "users", "columns": [ { "name": "id", "type": "integer", "nullable": false }, { "name": "email", "type": "text", "nullable": false }, { "name": "created_at", "type": "timestamp", "nullable": true } ] } ], "views": [ { "name": "active_users", "schema": "public" } ]}/customer/databases/{database_id}/observability Get database metrics and slow query samples.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/observability{ "summary": { "tps": 42.5, "latency_avg_ms": 3.2, "latency_p99_ms": 18.7, "active_connections": 3, "database_storage_bytes": 10485760, "fs_logical_bytes": 2097152 }, "samples": [ { "query": "SELECT * FROM events WHERE user_id = $1", "sample_count": 120, "error_count": 0, "latency_avg_ms": 245.0, "latency_p99_ms": 480.0, "latency_max_ms": 612.0, "last_seen_ms_ago": 5000 } ]}Batch Create
Section titled “Batch Create”/customer/databases/batch Create multiple databases in a single request. Not available on the hosted service yet: every request is rejected and no database is created.
| Field | Type | Required | Description |
|---|---|---|---|
databases | array | yes | Array of { name, region?, admin_password? } items |
project_id | string | no | Project ID to assign all databases to |
A well-formed request currently returns 400 with
{"message":"Batch create is not currently available"}. Create databases one at a time with
POST /customer/databases instead.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"databases": [{"name": "shard-1"}, {"name": "shard-2", "region": "us-east-1"}], "project_id": "proj-abc"}' \ https://api.db9.ai/customer/databases/batch{ "databases": [ { "name": "shard-1" }, { "name": "shard-2", "region": "us-east-1" } ], "project_id": "proj-abc"}{ "message": "Batch create is not currently available" }SQL Execution
Section titled “SQL Execution”/customer/databases/{database_id}/sql Execute a SQL query and return results as column names + row arrays.
| Field | Type | Required | Description |
|---|---|---|---|
query | string | no | Inline SQL query |
file_content | string | no | SQL content (alternative to query) |
Provide either query or file_content, not both.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "SELECT id, email FROM users LIMIT 2"}' \ https://api.db9.ai/customer/databases/db_01h9xx/sql{ "query": "SELECT id, email FROM users LIMIT 2" }{ "columns": [ { "name": "id", "type": "integer" }, { "name": "email", "type": "text" } ], "rows": [ [1, "alice@example.com"], [2, "bob@example.com"] ], "row_count": 2, "command": "SELECT"}/customer/databases/{database_id}/dump Export schema and data as SQL. Pass {"ddl_only": true} for schema-only export.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ddl_only": false}' \ https://api.db9.ai/customer/databases/db_01h9xx/dump{ "ddl_only": false }{ "sql": "-- DB9 dump\nCREATE TABLE users (...);\nINSERT INTO users VALUES (...);", "object_count": 3}Connect Tokens & Keys
Section titled “Connect Tokens & Keys”/customer/databases/{database_id}/connect-token Mint a short-lived JWT connect token. Pass {"role": "admin"} in the body.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"role": "admin"}' \ https://api.db9.ai/customer/databases/db_01h9xx/connect-token{ "role": "admin" }{ "host": "pg.d0000.db9.io", "port": 5432, "database": "postgres", "user": "ykfpouzs1hv9.admin", "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "fs_websocket_url": "wss://ws.d0000.db9.io:5480", "fs_grpc_endpoint": "fs.d0000.db9.io:9443", "tenant_backend": "juicefs", "expires_in_seconds": 600, "expires_at": "2026-10-26T16:07:43.615682075+00:00", "tls": "not_enforced"}/customer/databases/{database_id}/connect-keys Create a long-lived DB Connect Key. The key value is only returned once.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"role": "admin", "scopes": []}' \ https://api.db9.ai/customer/databases/db_01h9xx/connect-keys{ "role": "admin", "scopes": [] }{ "id": "ck_01h9xxxxxxxxxxxxxxxx", "name": "ci", "connect_key": "db9ck_xxxxxxxxxxxxxxxxxxxx", "role": "admin", "scopes": [], "created_at": "2026-01-15T10:00:00Z"}/customer/databases/{database_id}/connect-keys List all DB Connect Keys (metadata only — key values are never returned).
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/connect-keys[ { "id": "ck_01h9xxxxxxxxxxxxxxxx", "name": "ci", "role": "admin", "scopes": [], "created_at": "2026-01-15T10:00:00Z", "expires_at": "2027-01-15T10:00:00Z" }]/customer/databases/{database_id}/connect-keys/{key_id} Revoke a DB Connect Key immediately.
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/connect-keys/ck_01h9xx{ "message": "Connect key revoked" }Auth Configuration
Section titled “Auth Configuration”/customer/databases/{database_id}/auth-config Get the browser data-plane authentication configuration. Default auth_mode is byo_jwt.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/auth-config{ "auth_mode": "byo_jwt", "byo_jwt": { "jwks_url": "https://example.com/.well-known/jwks.json", "audience": "my-app", "issuer": "https://example.com", "subject_claim": "sub", "claims_allowlist": [], "claims_max_bytes": 4096 }, "created_at": "2026-01-15T10:00:00Z", "updated_at": "2026-01-15T10:00:00Z"}/customer/databases/{database_id}/auth-config Create or update the browser data-plane authentication configuration.
| Field | Type | Required | Description |
|---|---|---|---|
auth_mode | string | no | Authentication mode (e.g. byo_jwt) |
byo_jwt | object | no | Bring-your-own JWT configuration |
curl -X PUT -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"auth_mode": "byo_jwt", "byo_jwt": {"jwks_url": "https://example.com/.well-known/jwks.json", "audience": "my-app", "issuer": "https://example.com"}}' \ https://api.db9.ai/customer/databases/db_01h9xx/auth-config{ "auth_mode": "byo_jwt", "byo_jwt": { "jwks_url": "https://example.com/.well-known/jwks.json", "audience": "my-app", "issuer": "https://example.com", "subject_claim": "sub", "claims_allowlist": [], "claims_max_bytes": 4096 }}{ "auth_mode": "byo_jwt", "byo_jwt": { "jwks_url": "https://example.com/.well-known/jwks.json", "audience": "my-app", "issuer": "https://example.com", "subject_claim": "sub", "claims_allowlist": [], "claims_max_bytes": 4096 }, "created_at": "2026-01-15T10:00:00Z", "updated_at": "2026-01-15T10:00:00Z"}Publishable Keys
Section titled “Publishable Keys”/customer/databases/{database_id}/publishable-keys Create a publishable key for browser/client-side data access. Key value returned once only.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | Key name |
allowed_origins | string[] | no | CORS allowed origins |
exposed_schemas | string[] | no | Schemas accessible via this key |
exposed_tables | string[] | no | Tables accessible via this key |
rate_limit | object | no | Rate limiting (rps, burst) |
expires_in_days | number | no | Key expiration in days |
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "web-app", "allowed_origins": ["https://example.com"], "exposed_schemas": ["public"]}' \ https://api.db9.ai/customer/databases/db_01h9xx/publishable-keys{ "name": "web-app", "allowed_origins": ["https://example.com"], "exposed_schemas": ["public"], "exposed_tables": ["posts", "comments"], "rate_limit": { "rps": 100, "burst": 200 }, "expires_in_days": 90}{ "id": "pk_01h9xxxxxxxxxxxxxxxx", "publishable_key": "db9pk_xxxxxxxxxxxxxxxxxxxx", "name": "web-app", "allowed_origins": ["https://example.com"], "exposed_schemas": ["public"], "exposed_tables": ["posts", "comments"], "expires_at": "2026-04-15T10:00:00Z", "created_at": "2026-01-15T10:00:00Z"}/customer/databases/{database_id}/publishable-keys List all publishable keys (metadata only — key values are never returned).
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/publishable-keys[ { "id": "pk_01h9xxxxxxxxxxxxxxxx", "name": "web-app", "allowed_origins": ["https://example.com"], "exposed_schemas": ["public"], "exposed_tables": ["posts", "comments"], "expires_at": "2026-04-15T10:00:00Z", "created_at": "2026-01-15T10:00:00Z" }]/customer/databases/{database_id}/publishable-keys/{key_id} Revoke a publishable key immediately.
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/publishable-keys/ck_01h9xx{ "message": "Publishable key revoked" }Service Keys
Section titled “Service Keys”/customer/databases/{database_id}/service-keys Create a service key for server-side operations (CRUD only — consumption API not yet implemented). Key value returned once only.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/service-keys{ "id": "sk_01h9xxxxxxxxxxxxxxxx", "name": "service-key", "service_key": "db9sk_xxxxxxxxxxxxxxxxxxxx", "created_at": "2026-01-15T10:00:00Z"}/customer/databases/{database_id}/service-keys List all service keys (metadata only).
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/service-keys[ { "id": "sk_01h9xxxxxxxxxxxxxxxx", "name": "service-key", "created_at": "2026-01-15T10:00:00Z", "expires_at": "2027-01-15T10:00:00Z" }]/customer/databases/{database_id}/service-keys/{key_id} Revoke a service key immediately.
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/service-keys/ck_01h9xx{ "message": "Service key revoked" }Database Users
Section titled “Database Users”/customer/databases/{database_id}/users List all PostgreSQL users in the database.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/users[ { "name": "admin", "is_superuser": true, "can_login": true, "can_create_db": true, "can_create_role": true }, { "name": "app_user", "is_superuser": false, "can_login": true, "can_create_db": false, "can_create_role": false }]/customer/databases/{database_id}/users Create a new PostgreSQL user with the password you pass. A request without password is rejected with 400 password is required.
username in the request is the role name. The returned connection_string includes the tenant ID prefix required for PostgreSQL connections; use it as-is.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"username": "app_user", "password": "playground123"}' \ https://api.db9.ai/customer/databases/db_01h9xx/users{ "username": "app_user", "password": "playground123" }{ "message": "User 'app_user' created", "username": "app_user", "password": "playground123", "connection_string": "postgresql://ykfpouzs1hv9.app_user@pg.d0000.db9.io:5432/postgres", "connection_string_with_password": "postgresql://ykfpouzs1hv9.app_user:playground123@pg.d0000.db9.io:5432/postgres"}/customer/databases/{database_id}/users/{username} Delete a PostgreSQL user from the database.
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/users/app_user{ "message": "User 'app_user' deleted" }Migrations
Section titled “Migrations”/customer/databases/{database_id}/migrations List all applied migrations with names, checksums, and timestamps.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/migrations[ { "name": "add_users_table", "checksum": "sha256:abc123", "applied_at": "2026-01-15T10:00:00Z", "sql_preview": "CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT NOT NULL);" }]/customer/databases/{database_id}/migrations Apply a migration. Migrations are idempotent by name — re-applying the same name returns 409.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Migration name (must be unique) |
sql | string | yes | SQL to execute |
checksum | string | no | Content digest for integrity verification |
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "add_users_table", "sql": "CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT NOT NULL);"}' \ https://api.db9.ai/customer/databases/db_01h9xx/migrations{ "name": "add_users_table", "sql": "CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT NOT NULL);"}{ "status": "applied", "name": "add_users_table"}Branching
Section titled “Branching”/customer/databases/{database_id}/branch Create a branch — a full copy of the database schema (and optionally data) at a point in time.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Branch name |
snapshot_at | string | no | RFC 3339 UTC timestamp for point-in-time branching (omit to branch from current state) |
The branch is returned in state CLONING; poll
GET /customer/databases/{database_id} with the branch ID until state is ACTIVE.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "feature-branch"}' \ https://api.db9.ai/customer/databases/db_01h9xx/branch{ "name": "feature-branch", "snapshot_at": "2026-03-22T06:00:00Z"}{ "id": "p18ll9khu949", "name": "feature-branch", "state": "CLONING", "state_reason": "Branch restore in progress from TiKV snapshot 2026-10-26T15:43:38.833684729+00:00", "snapshot_at": "2026-10-26T15:43:38.833684729+00:00", "parent_database_id": "o85yaxxv329y", "project_id": "6bd5903d-...", "region": "us-west-2", "admin_user": "admin", "admin_password": "generated-password", "created_at": "2026-10-26T15:43:38.833688800+00:00", "last_active_at": null, "last_modified_at": null, "connection_string": "postgresql://p18ll9khu949.admin@pg.d0000.db9.io:5432/postgres", "fs_websocket_url": "wss://ws.d0000.db9.io:5480"}Backups & Restores
Section titled “Backups & Restores”/customer/databases/{database_id}/backups Create a backup of a database.
| Field | Type | Required | Description |
|---|---|---|---|
label | string | no | Human-readable label for the backup |
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"label": "pre-migration-snap"}' \ https://api.db9.ai/customer/databases/db_01h9xx/backups{ "label": "pre-migration-snap"}{ "id": "bkp_01...", "database_id": "db_01h9xxxxxxxxxxxxxxxx", "state": "creating", "label": "pre-migration-snap", "created_at": "2026-04-09T10:00:00Z"}/customer/databases/{database_id}/backups List all backups for a database.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/backups/customer/databases/{database_id}/backups/{backup_id} Get details of a specific backup.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/backups/bkp_01h9xx/customer/databases/{database_id}/backups/{backup_id} Delete a backup.
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/backups/bkp_01h9xx/customer/restores Restore a database from a backup. Creates a new database with the restored data.
| Field | Type | Required | Description |
|---|---|---|---|
backup_id | string | yes | ID of the backup to restore from |
target_name | string | yes | Name for the restored database |
idempotency_key | string | no | Idempotency key for safe retries |
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"backup_id": "bkp_01h9xx", "target_name": "myapp-restored"}' \ https://api.db9.ai/customer/restores{ "backup_id": "bkp_01...", "target_name": "myapp-restored"}/customer/restores/{restore_id} Check the status of a restore operation.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/restores/rst_01h9xxFilesystem
Section titled “Filesystem”/customer/databases/{database_id}/fs-connect Obtain database credentials and filesystem WebSocket URL for fs9 access.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/fs-connect{ "database_id": "ykfpouzs1hv9", "database_name": "myapp", "admin_user": "ykfpouzs1hv9.admin", "admin_password": "stored-password", "connection_string": "postgresql://ykfpouzs1hv9.admin@pg.d0000.db9.io:5432/postgres", "fs_websocket_url": "wss://ws.d0000.db9.io:5480", "connection_string_with_password": "postgresql://ykfpouzs1hv9.admin:stored-password@pg.d0000.db9.io:5432/postgres", "tenant_backend": "juicefs", "fs_endpoint": { "kind": "grpc", "url": "grpcs://fs.d0000.db9.io:9443", "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImF1dGg5LXByb2QtMSJ9...", "token_expires_at": "2026-10-26T16:02:00.841058760+00:00", "capabilities": ["presigned_uploads", "presigned_downloads", "subscribe", "batch_stat"] }}Functions
Section titled “Functions”/customer/databases/{database_id}/functions Deploy a serverless function. Functions run in an isolated environment with optional database access, secrets, and filesystem scope.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Function name |
entrypoint | string | yes | Exported function name — a bare identifier (alphanumeric, _ or $). A dotted form like index.handler is rejected with 400 invalid_entrypoint |
bundle_ref | string | yes | Bundle source. Must start with inline: followed by the module source; other sources are rejected with 400 invalid_bundle_ref. The runtime is CommonJS — assign the entrypoint to module.exports. export default creates successfully and then fails at invoke with Unexpected token 'export' |
bundle_digest | string | no | Not yet implemented. Sending any value is rejected with 400 bundle_digest_not_yet_supported; the response always returns "" |
run_as | string | no | invoker (default) or service — not a role name. Any other value is rejected with 400 invalid_run_as. An invoker function runs as the authenticated role carrying the caller’s JWT subject; a service function runs as service with no caller identity. service is only accepted together with cron_schedule — without it you get 403 service_create_forbidden |
bypass_rls | boolean | no | Not yet implemented. true is rejected with 400 bypass_rls_not_yet_supported; only false is accepted |
limits | object | no | Execution limits |
network_allowlist | string[] | no | Allowed outbound network destinations |
fs9_scope | array | no | Array of {"path": "...", "mode": "ro"|"rw"} entries, or {} for none. Any other shape is rejected with 400 invalid_fs9_scope |
secret_bindings | object | no | Map of environment alias to secret name, e.g. {"API_KEY": "MY_API_KEY"}. An array is rejected at deserialization, and an unknown secret gives 400 secret_not_found |
cron_schedule | string | no | Cron expression for scheduled execution. Requires run_as: "service"; pairing it with invoker gives 400 cron_requires_service |
visibility | string | no | "public" (callable via publishable key) or "private" (default) |
db_api | string | no | Result format of ctx.db.query: "sql-api" (default) returns { columns, rows, row_count, command }; "pg" returns node-postgres results and errors. See Runtime & ctx. Any other value is rejected with 400 invalid_db_api |
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "my-function", "entrypoint": "handler", "bundle_ref": "inline:module.exports = { handler: async () => ({ ok: 1 }) }"}' \ https://api.db9.ai/customer/databases/db_01h9xx/functions{ "name": "my-function", "entrypoint": "handler", "bundle_ref": "inline:module.exports = { handler: async () => ({ ok: 1 }) }", "run_as": "invoker", "bypass_rls": false, "limits": { "timeout_ms": 30000 }, "network_allowlist": ["https://api.example.com"], "fs9_scope": [{ "path": "/data", "mode": "ro" }], "secret_bindings": { "API_KEY": "MY_API_KEY" }}{ "function": { "id": "7c39a911-9cca-4d6a-ac94-ffd19090cf59", "name": "my-function", "enabled": true, "active_version_id": "ea702b76-3766-4ce0-b177-f05a6001154b", "visibility": "private", "created_at": "2026-01-15T10:00:00Z", "updated_at": "2026-01-15T10:00:00Z" }, "version": { "id": "ea702b76-3766-4ce0-b177-f05a6001154b", "version": 1, "entrypoint": "handler", "bundle_ref": "inline:module.exports = { handler: async () => ({ ok: 1 }) }", "bundle_digest": "", "run_as": "invoker", "bypass_rls": false, "db_api": "sql-api", "created_at": "2026-01-15T10:00:00Z" }}/customer/databases/{database_id}/functions/{function_id} Deploy a new version of a function and make it active immediately. Every update uploads the source again.
| Field | Type | Required | Description |
|---|---|---|---|
bundle_ref | string | yes | Function source, as in Create. A request without it is rejected with 422 |
The other Create fields — entrypoint, run_as, bypass_rls, limits, network_allowlist,
fs9_scope, secret_bindings, cron_schedule, visibility and db_api — are optional and
take the same values. Each one you omit keeps the active version’s value, so an update without
db_api keeps the current result format. name cannot be changed (the field is ignored), and
{function_id} must be the function’s ID: a name in the path returns 404 Function not found.
curl -X PUT -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"bundle_ref": "inline:module.exports = { handler: async () => ({ ok: 2 }) }", "db_api": "pg"}' \ https://api.db9.ai/customer/databases/db_01h9xx/functions/9bbf0cfc-8795-46da-a317-012e733a9309{ "function": { "id": "9bbf0cfc-8795-46da-a317-012e733a9309", "name": "my-function", "enabled": true, "active_version_id": "f5d657f9-ec87-47e2-8a0f-2bdc7c19a38d", "visibility": "private", "created_at": "2026-10-26T15:49:34.291060601+00:00", "updated_at": "2026-10-26T15:49:42.569993787+00:00" }, "version": { "id": "f5d657f9-ec87-47e2-8a0f-2bdc7c19a38d", "version": 2, "entrypoint": "handler", "bundle_ref": "inline:module.exports = { handler: async () => ({ ok: 2 }) }", "bundle_digest": "", "run_as": "invoker", "bypass_rls": false, "db_api": "pg", "created_at": "2026-10-26T15:49:42.568113421+00:00" }}/customer/databases/{database_id}/functions List all deployed functions.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/functions[ { "id": "7c39a911-9cca-4d6a-ac94-ffd19090cf59", "name": "my-function", "enabled": true, "active_version_id": "ea702b76-3766-4ce0-b177-f05a6001154b", "visibility": "private", "created_at": "2026-01-15T10:00:00Z", "updated_at": "2026-01-15T10:00:00Z" }]/customer/databases/{database_id}/functions/{function_id}/invoke Invoke a function synchronously and return the output.
| Field | Type | Required | Description |
|---|---|---|---|
input | object | no | Function input payload |
idempotency_key | string | no | Unique key for idempotent invocations |
The call returns HTTP 200 whether or not the run succeeds, so check status: succeeded,
failed, or timed_out, the last two with error_code and error_message. result_json is
the handler’s return value as a JSON string, absent when the handler returns undefined; a
value larger than 256 KiB fails the run with result_too_large. logs_tail holds the run’s
console output and is absent when the function printed nothing — see
Troubleshooting.
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"input": {"user_id": "u_123"}}' \ https://api.db9.ai/customer/databases/db_01h9xx/functions/fn_01h9xx/invoke{ "input": { "user_id": "u_123" } }{ "run_id": "3a387c6c-901b-4899-a4b8-af8b5e7d5923", "function_id": "e26feb7a-79cf-441d-950e-d09086a082e3", "version_id": "fc17d8dc-90d4-4703-8378-517fa7d31bd0", "status": "succeeded", "result_json": "{\"ok\":true}", "logs_tail": "[stdout] hello 1\n[stderr] oops\n[stderr] careful\n[stdout] fyi"}{ "run_id": "ccd6a14d-0f8d-4536-8658-f7fc2d62601b", "function_id": "4a3c4061-001f-4801-be41-bc6b0a95b59c", "version_id": "44ffcd1f-5914-4a3b-aecf-27ebb8a0c57e", "status": "failed", "error_code": "result_too_large", "error_message": "Function result exceeds 262144 bytes"}/customer/databases/{database_id}/functions/{function_id}/runs List a function's most recent runs, newest first.
| Query parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | no | Number of runs to return: 50 by default, at most 200 |
Each entry is a summary. result_json, error_message and logs_tail are included only when
they are 4 KiB or smaller; a larger one is left out and named in omitted_fields — fetch the
run itself to read it. The run’s input is never returned.
curl -H "Authorization: Bearer $DB9_API_KEY" \ "https://api.db9.ai/customer/databases/db_01h9xx/functions/fn_01h9xx/runs?limit=2"[ { "id": "4853c5f6-451f-40c8-8bca-127e79d6f510", "function_id": "4a3c4061-001f-4801-be41-bc6b0a95b59c", "version_id": "44ffcd1f-5914-4a3b-aecf-27ebb8a0c57e", "trigger_type": "invoke", "status": "succeeded", "run_as": "invoker", "attempt": 1, "created_at": "2026-10-26T15:45:46.647439069+00:00", "started_at": "2026-10-26T15:45:47.156507356+00:00", "finished_at": "2026-10-26T15:45:47.214503289+00:00", "result_json": "{\"n\":1000}", "omitted_fields": ["logs_tail"] }, { "id": "69bb5c55-ca26-46f1-bc5a-b2548a1eeb9d", "function_id": "4a3c4061-001f-4801-be41-bc6b0a95b59c", "version_id": "44ffcd1f-5914-4a3b-aecf-27ebb8a0c57e", "trigger_type": "invoke", "status": "failed", "run_as": "invoker", "attempt": 1, "created_at": "2026-10-26T15:45:39.716336086+00:00", "started_at": "2026-10-26T15:45:40.156563830+00:00", "finished_at": "2026-10-26T15:45:40.189785900+00:00", "error_code": "execution_error", "error_message": "boom" }]/customer/databases/{database_id}/functions/{function_id}/runs/{run_id} Get details for a specific function run including output and timing.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/functions/fn_01h9xx/runs/run_01h9xx{ "id": "8de28f77-dfa4-487f-91fc-64f2aae00f9d", "function_id": "e26feb7a-79cf-441d-950e-d09086a082e3", "version_id": "fc17d8dc-90d4-4703-8378-517fa7d31bd0", "trigger_type": "invoke", "status": "succeeded", "run_as": "invoker", "attempt": 1, "created_at": "2026-10-26T15:45:27.629589483+00:00", "started_at": "2026-10-26T15:45:28.157297811+00:00", "finished_at": "2026-10-26T15:45:28.191387664+00:00", "result_json": "{\"ok\":true}", "logs_tail": "[stdout] hello 1\n[stderr] oops\n[stderr] careful\n[stdout] fyi"}/customer/databases/{database_id}/functions/{function_id}/runs/{run_id}/logs Get the console output of a specific function run. logs is null when the run printed nothing.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/functions/fn_01h9xx/runs/run_01h9xx/logs{ "logs": "[stdout] hello 1\n[stderr] oops\n[stderr] careful\n[stdout] fyi", "run_id": "8de28f77-dfa4-487f-91fc-64f2aae00f9d"}Secrets
Section titled “Secrets”/customer/databases/{database_id}/secrets List all secret names and timestamps. Secret values are never returned.
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/secrets[ { "name": "API_KEY", "created_at": "2026-01-15T10:00:00Z", "updated_at": "2026-01-20T08:00:00Z" }]/customer/databases/{database_id}/secrets Create a secret. The value is encrypted at rest and never returned after creation.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Secret name (alphanumeric and underscores) |
value | string | yes | Secret value (encrypted at rest) |
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "API_KEY", "value": "sk-live-xxxxxxxxxxxx"}' \ https://api.db9.ai/customer/databases/db_01h9xx/secrets{ "name": "API_KEY", "value": "sk-live-xxxxxxxxxxxx" }{ "name": "API_KEY", "created_at": "2026-01-15T10:00:00Z"}/customer/databases/{database_id}/secrets/{secret_name} Update an existing secret's value.
| Field | Type | Required | Description |
|---|---|---|---|
value | string | yes | New secret value |
curl -X PUT -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"value": "sk-live-new-xxxxxxxxxxxx"}' \ https://api.db9.ai/customer/databases/db_01h9xx/secrets/API_KEY{ "value": "sk-live-new-xxxxxxxxxxxx" }{ "name": "API_KEY", "updated_at": "2026-02-01T09:00:00Z"}/customer/databases/{database_id}/secrets/{secret_name} Delete a secret permanently.
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/databases/db_01h9xx/secrets/API_KEYResponse: 204 No Content — empty body.
API Tokens
Section titled “API Tokens”/customer/tokens Create an API token. The token value is returned once only — store it securely.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | Token name |
expires_in_days | integer | no | Expiry in days (default: 365) |
scope_json | string | no | JSON string scoping token to specific databases, e.g. {"databases":[{"id":"<id>","access":"ro"}]} |
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "ci-token", "expires_in_days": 365}' \ https://api.db9.ai/customer/tokens{ "name": "ci-token", "expires_in_days": 365}{ "id": "tok_01h9xxxxxxxxxxxxxxxx", "name": "ci-token", "token": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "expires_at": "2027-01-15T10:00:00Z", "created_at": "2026-01-15T10:00:00Z"}/customer/tokens List all API tokens (metadata only — token values are never returned).
curl -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/tokens[ { "id": "tok_01h9xxxxxxxxxxxxxxxx", "name": "ci-token", "expires_at": "2027-01-15T10:00:00Z", "created_at": "2026-01-15T10:00:00Z" }]/customer/tokens/{token_id} Revoke an API token immediately. All requests using this token will be rejected.
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \ https://api.db9.ai/customer/tokens/tok_01h9xx{ "message": "Token revoked" }Next Steps
Section titled “Next Steps”- Error Codes — Full HTTP status code reference with causes and resolution
- CLI Reference — Command-line interface
- TypeScript SDK — Server-side SDK wrapping this API
- Browser SDK — Client-side data access
- Connect — Connection strings and drivers