Skip to content
Discord Get Started

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.

Most requests require a Bearer token in the Authorization header:

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" https://api.db9.ai/customer/databases

Exceptions: 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)

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.

Error Response Shape
{
"message": "human-readable message describing what went wrong"
}
StatusMeaning
400Bad request — missing or invalid fields
401Unauthorized — missing or invalid token
403Forbidden — token lacks required scope
404Not found — resource does not exist
409Conflict — resource already exists
422Unprocessable — valid JSON but semantic error
429Rate limited — slow down and retry
500Internal server error
GET /customer/me

Return the current customer's identity and profile.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/me
Response
{
"id": "cus_01h9xxxxxxxxxxxxxxxx",
"email": "user@example.com",
"created_at": "2026-01-15T10:00:00Z",
"status": "active"
}
POST /customer/anonymous-register

Create an anonymous account and return a session token. No credentials required.

Terminal
curl -X POST https://api.db9.ai/customer/anonymous-register
Response
{
"anonymous_id": "anon_01h9xxxxxxxxxxxxxxxx",
"anonymous_secret": "sk_anon_xxxxxxxxxxxxxxxxxxxx",
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_at": "2027-01-15T10:00:00Z",
"is_anonymous": true
}
POST /customer/anonymous-refresh

Refresh an anonymous session token using the customer ID and secret.

FieldTypeRequiredDescription
anonymous_idstringyesAnonymous customer ID
anonymous_secretstringyesAnonymous account secret
Terminal
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
Request
{ "anonymous_id": "anon_01h9xxxxxxxxxxxxxxxx", "anonymous_secret": "sk_anon_xxxxxxxxxxxxxxxxxxxx" }
Response
{
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_at": "2027-02-15T10:00:00Z"
}
POST /customer/claim

Upgrade an anonymous account to a verified identity using an Auth0 id_token. Requires a Bearer token.

Terminal
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
Request
{ "id_token": "<Auth0 id_token>" }
Response
{
"id": "cus_01h9xxxxxxxxxxxxxxxx",
"email": "user@example.com",
"claimed": true
}
POST /customer/adopt-anonymous-databases/preflight

Pre-check eligibility and quotas before adopting databases from an anonymous account.

FieldTypeRequiredDescription
anonymous_customer_idstringyesCustomer ID of the anonymous account
anonymous_secretstringyesSecret for the anonymous account
Terminal
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
Request
{
"anonymous_customer_id": "anon_01h9xxxxxxxxxxxxxxxx",
"anonymous_secret": "sk_anon_xxxxxxxxxxxxxxxxxxxx"
}
Response
{
"databases": [
{ "id": "db_01h9xxxxxxxxxxxxxxxx", "name": "myapp", "state": "ACTIVE" }
],
"quota_ok": true,
"current_count": 1,
"database_limit": 10
}
POST /customer/adopt-anonymous-databases

Transfer databases from an anonymous account to your authenticated account.

FieldTypeRequiredDescription
anonymous_customer_idstringyesCustomer ID of the anonymous account
anonymous_secretstringyesSecret for the anonymous account
database_idsstring[]yesList of database IDs to adopt (max 100)
idempotency_keystringyesUnique key for idempotent retries
Terminal
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
Request
{
"anonymous_customer_id": "anon_01h9xxxxxxxxxxxxxxxx",
"anonymous_secret": "sk_anon_xxxxxxxxxxxxxxxxxxxx",
"database_ids": ["db_01h9xxxxxxxxxxxxxxxx"],
"idempotency_key": "adopt-2026-01-15"
}
Response
{
"transferred": ["db_01h9xxxxxxxxxxxxxxxx"],
"skipped": [],
"total_transferred": 1
}

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.

POST /customer/databases

Create a new database. The response includes the admin password; its connection string does not.

FieldTypeRequiredDescription
namestringyesDatabase name
regionstringnoDeployment region
admin_passwordstringnoAdmin password (auto-generated if omitted)
project_idstringnoProject 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.

Terminal
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
Request
{
"name": "myapp",
"region": "us-east-1"
}
Response
{
"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"
}
GET /customer/databases

List all databases in your account.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases
Response
[
{
"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"
}
]
GET /customer/databases/{database_id}

Get database details including connection string and current state.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx
Response
{
"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"
}
DELETE /customer/databases/{database_id}

Permanently delete a database. This action is irreversible.

Terminal
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx
Response
{ "message": "Database disabled" }
POST /customer/databases/{database_id}/reset-password

Reset the admin password for a database and return the new credentials.

Terminal
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/reset-password
Response
{
"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"
}
GET /customer/databases/{database_id}/credentials

Retrieve stored admin credentials for a database.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/credentials
Response
{
"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"
}
GET /customer/databases/{database_id}/schema

Get the database schema — tables, columns, and types.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/schema
Response
{
"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" }
]
}
GET /customer/databases/{database_id}/observability

Get database metrics and slow query samples.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/observability
Response
{
"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
}
]
}
POST /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.

FieldTypeRequiredDescription
databasesarrayyesArray of { name, region?, admin_password? } items
project_idstringnoProject 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.

Terminal
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
Request
{
"databases": [
{ "name": "shard-1" },
{ "name": "shard-2", "region": "us-east-1" }
],
"project_id": "proj-abc"
}
Response (400)
{ "message": "Batch create is not currently available" }
POST /customer/databases/{database_id}/sql

Execute a SQL query and return results as column names + row arrays.

FieldTypeRequiredDescription
querystringnoInline SQL query
file_contentstringnoSQL content (alternative to query)

Provide either query or file_content, not both.

Terminal
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
Request
{ "query": "SELECT id, email FROM users LIMIT 2" }
Response
{
"columns": [
{ "name": "id", "type": "integer" },
{ "name": "email", "type": "text" }
],
"rows": [
[1, "alice@example.com"],
[2, "bob@example.com"]
],
"row_count": 2,
"command": "SELECT"
}
POST /customer/databases/{database_id}/dump

Export schema and data as SQL. Pass {"ddl_only": true} for schema-only export.

Terminal
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
Request
{ "ddl_only": false }
Response
{
"sql": "-- DB9 dump\nCREATE TABLE users (...);\nINSERT INTO users VALUES (...);",
"object_count": 3
}
POST /customer/databases/{database_id}/connect-token

Mint a short-lived JWT connect token. Pass {"role": "admin"} in the body.

Terminal
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
Request
{ "role": "admin" }
Response
{
"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"
}
POST /customer/databases/{database_id}/connect-keys

Create a long-lived DB Connect Key. The key value is only returned once.

Terminal
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
Request
{ "role": "admin", "scopes": [] }
Response
{
"id": "ck_01h9xxxxxxxxxxxxxxxx",
"name": "ci",
"connect_key": "db9ck_xxxxxxxxxxxxxxxxxxxx",
"role": "admin",
"scopes": [],
"created_at": "2026-01-15T10:00:00Z"
}
GET /customer/databases/{database_id}/connect-keys

List all DB Connect Keys (metadata only — key values are never returned).

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/connect-keys
Response
[
{
"id": "ck_01h9xxxxxxxxxxxxxxxx",
"name": "ci",
"role": "admin",
"scopes": [],
"created_at": "2026-01-15T10:00:00Z",
"expires_at": "2027-01-15T10:00:00Z"
}
]
DELETE /customer/databases/{database_id}/connect-keys/{key_id}

Revoke a DB Connect Key immediately.

Terminal
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/connect-keys/ck_01h9xx
Response
{ "message": "Connect key revoked" }
GET /customer/databases/{database_id}/auth-config

Get the browser data-plane authentication configuration. Default auth_mode is byo_jwt.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/auth-config
Response
{
"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"
}
PUT /customer/databases/{database_id}/auth-config

Create or update the browser data-plane authentication configuration.

FieldTypeRequiredDescription
auth_modestringnoAuthentication mode (e.g. byo_jwt)
byo_jwtobjectnoBring-your-own JWT configuration
Terminal
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
Request
{
"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
}
}
Response
{
"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"
}
POST /customer/databases/{database_id}/publishable-keys

Create a publishable key for browser/client-side data access. Key value returned once only.

FieldTypeRequiredDescription
namestringnoKey name
allowed_originsstring[]noCORS allowed origins
exposed_schemasstring[]noSchemas accessible via this key
exposed_tablesstring[]noTables accessible via this key
rate_limitobjectnoRate limiting (rps, burst)
expires_in_daysnumbernoKey expiration in days
Terminal
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
Request
{
"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
}
Response
{
"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"
}
GET /customer/databases/{database_id}/publishable-keys

List all publishable keys (metadata only — key values are never returned).

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/publishable-keys
Response
[
{
"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"
}
]
DELETE /customer/databases/{database_id}/publishable-keys/{key_id}

Revoke a publishable key immediately.

Terminal
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/publishable-keys/ck_01h9xx
Response
{ "message": "Publishable key revoked" }
POST /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.

Terminal
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/service-keys
Response
{
"id": "sk_01h9xxxxxxxxxxxxxxxx",
"name": "service-key",
"service_key": "db9sk_xxxxxxxxxxxxxxxxxxxx",
"created_at": "2026-01-15T10:00:00Z"
}
GET /customer/databases/{database_id}/service-keys

List all service keys (metadata only).

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/service-keys
Response
[
{
"id": "sk_01h9xxxxxxxxxxxxxxxx",
"name": "service-key",
"created_at": "2026-01-15T10:00:00Z",
"expires_at": "2027-01-15T10:00:00Z"
}
]
DELETE /customer/databases/{database_id}/service-keys/{key_id}

Revoke a service key immediately.

Terminal
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/service-keys/ck_01h9xx
Response
{ "message": "Service key revoked" }
GET /customer/databases/{database_id}/users

List all PostgreSQL users in the database.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/users
Response
[
{ "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 }
]
POST /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.

Terminal
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
Request
{ "username": "app_user", "password": "playground123" }
Response
{
"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"
}
DELETE /customer/databases/{database_id}/users/{username}

Delete a PostgreSQL user from the database.

Terminal
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/users/app_user
Response
{ "message": "User 'app_user' deleted" }
GET /customer/databases/{database_id}/migrations

List all applied migrations with names, checksums, and timestamps.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/migrations
Response
[
{
"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);"
}
]
POST /customer/databases/{database_id}/migrations

Apply a migration. Migrations are idempotent by name — re-applying the same name returns 409.

FieldTypeRequiredDescription
namestringyesMigration name (must be unique)
sqlstringyesSQL to execute
checksumstringnoContent digest for integrity verification
Terminal
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
Request
{
"name": "add_users_table",
"sql": "CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT NOT NULL);"
}
Response
{
"status": "applied",
"name": "add_users_table"
}
POST /customer/databases/{database_id}/branch

Create a branch — a full copy of the database schema (and optionally data) at a point in time.

FieldTypeRequiredDescription
namestringyesBranch name
snapshot_atstringnoRFC 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.

Terminal
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
Request
{
"name": "feature-branch",
"snapshot_at": "2026-03-22T06:00:00Z"
}
Response
{
"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"
}
POST /customer/databases/{database_id}/backups

Create a backup of a database.

FieldTypeRequiredDescription
labelstringnoHuman-readable label for the backup
Terminal
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
Request
{
"label": "pre-migration-snap"
}
Response
{
"id": "bkp_01...",
"database_id": "db_01h9xxxxxxxxxxxxxxxx",
"state": "creating",
"label": "pre-migration-snap",
"created_at": "2026-04-09T10:00:00Z"
}
GET /customer/databases/{database_id}/backups

List all backups for a database.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/backups
GET /customer/databases/{database_id}/backups/{backup_id}

Get details of a specific backup.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/backups/bkp_01h9xx
DELETE /customer/databases/{database_id}/backups/{backup_id}

Delete a backup.

Terminal
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/backups/bkp_01h9xx
POST /customer/restores

Restore a database from a backup. Creates a new database with the restored data.

FieldTypeRequiredDescription
backup_idstringyesID of the backup to restore from
target_namestringyesName for the restored database
idempotency_keystringnoIdempotency key for safe retries
Terminal
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
Request
{
"backup_id": "bkp_01...",
"target_name": "myapp-restored"
}
GET /customer/restores/{restore_id}

Check the status of a restore operation.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/restores/rst_01h9xx
POST /customer/databases/{database_id}/fs-connect

Obtain database credentials and filesystem WebSocket URL for fs9 access.

Terminal
curl -X POST -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/fs-connect
Response
{
"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"]
}
}
POST /customer/databases/{database_id}/functions

Deploy a serverless function. Functions run in an isolated environment with optional database access, secrets, and filesystem scope.

FieldTypeRequiredDescription
namestringyesFunction name
entrypointstringyesExported function name — a bare identifier (alphanumeric, _ or $). A dotted form like index.handler is rejected with 400 invalid_entrypoint
bundle_refstringyesBundle 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_digeststringnoNot yet implemented. Sending any value is rejected with 400 bundle_digest_not_yet_supported; the response always returns ""
run_asstringnoinvoker (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_rlsbooleannoNot yet implemented. true is rejected with 400 bypass_rls_not_yet_supported; only false is accepted
limitsobjectnoExecution limits
network_allowliststring[]noAllowed outbound network destinations
fs9_scopearraynoArray of {"path": "...", "mode": "ro"|"rw"} entries, or {} for none. Any other shape is rejected with 400 invalid_fs9_scope
secret_bindingsobjectnoMap 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_schedulestringnoCron expression for scheduled execution. Requires run_as: "service"; pairing it with invoker gives 400 cron_requires_service
visibilitystringno"public" (callable via publishable key) or "private" (default)
db_apistringnoResult 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
Terminal
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
Request
{
"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" }
}
Response
{
"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"
}
}
PUT /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.

FieldTypeRequiredDescription
bundle_refstringyesFunction 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.

Terminal
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
Response
{
"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"
}
}
GET /customer/databases/{database_id}/functions

List all deployed functions.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/functions
Response
[
{
"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"
}
]
POST /customer/databases/{database_id}/functions/{function_id}/invoke

Invoke a function synchronously and return the output.

FieldTypeRequiredDescription
inputobjectnoFunction input payload
idempotency_keystringnoUnique 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.

Terminal
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
Request
{ "input": { "user_id": "u_123" } }
Response
{
"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"
}
Response (failed run)
{
"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"
}
GET /customer/databases/{database_id}/functions/{function_id}/runs

List a function's most recent runs, newest first.

Query parameterTypeRequiredDescription
limitintegernoNumber 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.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
"https://api.db9.ai/customer/databases/db_01h9xx/functions/fn_01h9xx/runs?limit=2"
Response
[
{
"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"
}
]
GET /customer/databases/{database_id}/functions/{function_id}/runs/{run_id}

Get details for a specific function run including output and timing.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/functions/fn_01h9xx/runs/run_01h9xx
Response
{
"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"
}
GET /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.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/functions/fn_01h9xx/runs/run_01h9xx/logs
Response
{
"logs": "[stdout] hello 1\n[stderr] oops\n[stderr] careful\n[stdout] fyi",
"run_id": "8de28f77-dfa4-487f-91fc-64f2aae00f9d"
}
GET /customer/databases/{database_id}/secrets

List all secret names and timestamps. Secret values are never returned.

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/secrets
Response
[
{
"name": "API_KEY",
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-20T08:00:00Z"
}
]
POST /customer/databases/{database_id}/secrets

Create a secret. The value is encrypted at rest and never returned after creation.

FieldTypeRequiredDescription
namestringyesSecret name (alphanumeric and underscores)
valuestringyesSecret value (encrypted at rest)
Terminal
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
Request
{ "name": "API_KEY", "value": "sk-live-xxxxxxxxxxxx" }
Response
{
"name": "API_KEY",
"created_at": "2026-01-15T10:00:00Z"
}
PUT /customer/databases/{database_id}/secrets/{secret_name}

Update an existing secret's value.

FieldTypeRequiredDescription
valuestringyesNew secret value
Terminal
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
Request
{ "value": "sk-live-new-xxxxxxxxxxxx" }
Response
{
"name": "API_KEY",
"updated_at": "2026-02-01T09:00:00Z"
}
DELETE /customer/databases/{database_id}/secrets/{secret_name}

Delete a secret permanently.

Terminal
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/databases/db_01h9xx/secrets/API_KEY

Response: 204 No Content — empty body.

POST /customer/tokens

Create an API token. The token value is returned once only — store it securely.

FieldTypeRequiredDescription
namestringnoToken name
expires_in_daysintegernoExpiry in days (default: 365)
scope_jsonstringnoJSON string scoping token to specific databases, e.g. {"databases":[{"id":"<id>","access":"ro"}]}
Terminal
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
Request
{
"name": "ci-token",
"expires_in_days": 365
}
Response
{
"id": "tok_01h9xxxxxxxxxxxxxxxx",
"name": "ci-token",
"token": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"expires_at": "2027-01-15T10:00:00Z",
"created_at": "2026-01-15T10:00:00Z"
}
GET /customer/tokens

List all API tokens (metadata only — token values are never returned).

Terminal
curl -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/tokens
Response
[
{
"id": "tok_01h9xxxxxxxxxxxxxxxx",
"name": "ci-token",
"expires_at": "2027-01-15T10:00:00Z",
"created_at": "2026-01-15T10:00:00Z"
}
]
DELETE /customer/tokens/{token_id}

Revoke an API token immediately. All requests using this token will be rejected.

Terminal
curl -X DELETE -H "Authorization: Bearer $DB9_API_KEY" \
https://api.db9.ai/customer/tokens/tok_01h9xx
Response
{ "message": "Token revoked" }