API overview
The CapyDB control plane is a plain HTTP API documented by an OpenAPI spec. Everything the dashboard and CLI do, you can do.
Base URL and spec
https://capydb.dev/api/capydbAll endpoints are under /v1. The OpenAPI document is public:
GET https://capydb.dev/api/capydb/v1/openapi.jsonThe per-tag pages in this section are generated from that document, so they cannot drift from the implementation. The TypeScript SDK is generated from the same spec.
Authentication & API keys
Programmatic callers authenticate with an API key, either as a bearer token or a header:
curl -H "Authorization: Bearer capy_live_..." https://capydb.dev/api/capydb/v1/projects
curl -H "X-API-Key: capy_live_..." https://capydb.dev/api/capydb/v1/projectsKeys belong to an organization and are created in the dashboard (Settings → API keys) or via POST /v1/organizations/{orgID}/api-keys. The plaintext is shown once; only a SHA-256 hash is stored. Keys can carry an expiry and are revocable without touching anything else.
The one unauthenticated corner
Creating, reading and destroying an ephemeral database needs no API key at all - that is the point of it. POST /v1/ephemeral-databases returns a claim token, and GET and DELETE /v1/ephemeral-databases/{projectID} accept that token in an X-CapyDB-Claim-Token header. Claiming one into an organization is an ordinary authenticated call and needs projects:write; a project-scoped key cannot claim, because a claim adds a project to the organization.
Scopes
Each key carries explicit scopes: projects:read, projects:write, credentials:read, backups:read, backups:write, jobs:read, organizations:read, organizations:write, api_keys:read, api_keys:write. Grant what the consumer needs, nothing else.
Project-scoped keys
A key created with a project_id is restricted to that single project:
- it can only act on that project (and its previews, backups, jobs)
- it cannot touch sibling projects
- org-management scopes (
organizations:write,api_keys:write, ...) are rejected on it
Use project-scoped keys for CI and integrations - the GitHub Action, deploy scripts, anything whose blast radius should be one project. A leaked project-scoped key is an incident; a leaked org key is a worse one.
Human sessions
Browser and CLI sessions are Clerk-authenticated; the active Clerk organization resolves which org you act in. Some actions are gated to the Clerk org admin role when called with a session: project create/delete, overwrite-restores, and API-key create/revoke.
Asynchronous jobs
Lifecycle actions (provision, backup, restore, import, preview create/reset/delete, credential rotation, deletion) return 202 Accepted with a job object instead of blocking. Poll GET /v1/jobs/{jobID} until completed or failed:
curl -s -H "X-API-Key: $CAPYDB_API_KEY" \
https://capydb.dev/api/capydb/v1/jobs/job_... | jq '.job.state'Or subscribe to webhooks (job.completed, job.failed, and the higher-level *.ready/*.completed events) instead of polling.
Conventions
- List endpoints always return JSON arrays - never
null. - Errors are
{"error": "message"}with an appropriate 4xx/5xx status. - Rate limiting is applied per IP (with a stricter bucket on the CLI-login endpoints). Back off on
429.
Quick tour
# Who am I?
curl -s -H "X-API-Key: $KEY" $BASE/v1/me
# Create a clone preview and wait for it
JOB=$(curl -s -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{"name":"pr-42","mode":"clone","ttl_hours":72}' \
$BASE/v1/projects/$PROJECT/preview-databases | jq -r '.job.id')
curl -s -H "X-API-Key: $KEY" $BASE/v1/jobs/$JOB | jq '.job.state'
# Fetch its connection strings once ready
curl -s -H "X-API-Key: $KEY" $BASE/v1/preview-databases/$PREVIEW/connections