Branches API
Base URL and authentication are described in the API overview. In the tables, “Action” is the permission name checked for the call.
The branch object
Section titled “The branch object”{ "name": "feature-x", "id": "alice/feature-x", "owner": "alice@example.com", "access": "private", "status": "ready", "status_reason": null, "base": "prod", "snapshot": "s20261109", "runtime": "postgres@18.6", "node": "node1", "created_at": "2026-11-09T12:00:00Z", "expires_at": "2026-11-12T12:00:00Z", "reset_at": null, "storage": { "used": 1048576, "referenced": 83886080 }, "connection": { "host": "node1", "port": 41203, "user": "feature-x" }, "proxy": { "host": "node1", "port": 6133, "user": "feature-x" }, "operation": null}| Field | Meaning |
|---|---|
status |
provisioning, ready, resetting, deleting, failed, or lost (its node is gone; see recreate) |
status_reason |
Why a branch is failed or lost, otherwise null |
id |
owner/name when the branch has an owner (an email owner is shortened to its local part), otherwise the name |
access |
private (own password; owner and admins) or open (the cluster’s shared password; anyone may connect) |
snapshot |
The snapshot of the base the branch was cloned from |
expires_at |
When the branch is deleted automatically; null for no expiry |
storage |
Bytes written by the branch (used) and bytes it can read (referenced), or null if unknown |
connection |
Direct connection: the node’s host, the branch’s own port and its user (the database name is not included; use /dsn) |
proxy |
Connection through the node’s proxy by user name, or null when the node has no proxy |
operation |
The operation running on the branch now (id, kind), or null |
GET /v1/branches
Section titled “GET /v1/branches”Action branch.read. Lists all branches, sorted by name.
{ "items": [ { "name": "feature-x", "status": "ready", "...": "..." } ], "next_cursor": null }POST /v1/branches
Section titled “POST /v1/branches”Action branch.create. Clones a base into a new branch. Answers 202 with an operation of kind branch.create.
| Field | Type | Meaning |
|---|---|---|
name |
string, required | Branch name |
base |
string, required | Base to clone |
snapshot |
string | Snapshot to clone. Default: the base’s latest |
ttl |
string | Lifetime, such as 72h. Default: the cluster’s Default TTL setting, or no expiry |
init_sql |
string | SQL to run once after cloning. A reset returns to this state |
node |
string | Node to place the branch on. Default: chosen by load, among nodes that hold the snapshot |
owner |
string | Who manages the branch. Default: the caller. Only an admin may name someone else |
access |
string | private (default) or open |
Errors: 404 not_found (no such base), 409 already_exists, 503 no_capacity. Open branches and the shared password are set in Settings.
Callers without a login can create a branch only from a snapshot marked public. Such a branch is always open, its TTL is capped at the Anonymous branch TTL, and only a limited number may exist (Settings: Anonymous branches).
GET /v1/branches/{name}
Section titled “GET /v1/branches/{name}”Action branch.read. Returns one branch.
PATCH /v1/branches/{name}
Section titled “PATCH /v1/branches/{name}”Action branch.extend. Sets the expiry to now plus ttl. Answers 200 with the branch.
{ "ttl": "24h" }DELETE /v1/branches/{name}
Section titled “DELETE /v1/branches/{name}”Action branch.delete. Answers 202 with an operation of kind branch.delete. A branch can be deleted when it is ready, failed or lost. Deleting a lost branch is recorded and finished when its node returns.
POST /v1/branches/{name}/reset
Section titled “POST /v1/branches/{name}/reset”Action branch.reset. Discards every change and returns the branch to its state right after creation (after init_sql). Answers 202, operation kind branch.reset. The branch must be ready; otherwise 409 conflict.
POST /v1/branches/{name}/recreate
Section titled “POST /v1/branches/{name}/recreate”Action branch.recreate. Builds a lost branch again on a node that holds its snapshot. Answers 202, operation kind branch.recreate. Optional query parameter node picks the node; without it one is chosen. Data written to the branch after it was created is gone.
GET /v1/branches/{name}/dsn
Section titled “GET /v1/branches/{name}/dsn”Action branch.connect. Returns the connection URL, including the password. Each call is written to the audit log.
{ "dsn": "postgres://feature-x:s3cret@node1:41203/postgres" }With ?via=proxy the URL points at the node’s proxy port instead. If the node has no proxy, or the branch has no user of its own yet, the answer is 409 not_routable.
POST /v1/branches/{name}/query
Section titled “POST /v1/branches/{name}/query”Action branch.query. Runs one SQL statement on a ready branch as the branch’s own user.
| Field | Type | Meaning |
|---|---|---|
sql |
string, required | One statement |
max_rows |
number | Rows to return, at most 1000 (the default) |
{ "columns": ["id", "email"], "rows": [["1", "user1@example.invalid"], ["2", null]], "truncated": false, "millis": 4}Values are strings, and SQL NULL is null. A statement without rows returns affected (the number of rows changed) instead of columns and rows. A failing statement returns 422 query_failed. A branch that is not ready returns 409 conflict.
Ownership
Section titled “Ownership”An admin (or a full token) can use every branch. Other callers can read all branches but change, connect to and query only their own. The exception is an open branch, which anyone may connect to and query. A paid module can also let teammates share branches. Other callers get 403 forbidden.