management api

create, list and delete databases, branch them, and rotate a credential — over the same bearer-token http as everything else. read the authorisation section first: three gates have to line up, and a 403 does not tell you which one is shut.

the shape of it

Same bearer scheme as sql over http, same one-shape error envelope, and no CORS headers here either — these routes are for your server, not your front end. The one thing that differs is size: a management request body is capped at 8 KB, not the 4 MB the SQL endpoint allows, because nothing you send here is bigger than a name.

bash

curl -sS https://briven.tech/api/v1/databases \
  -H "Authorization: Bearer $BRIVEN_API_KEY"

response

{
  "items": [
    {
      "id": "db_...",
      "name": "notes",
      "status": "ready",
      "failureReason": null,
      "hostKind": "shared",
      "createdAt": "2026-01-09T10:14:22.000Z"
    }
  ]
}

the three gates

This is the section people need and skip. A key that runs SQL perfectly can still be refused every management call, and the reason is always one of these three.

  • 1 · scopeA key confined to a single database is refused every management call — including the read-only ones — with 403 key_scope_too_narrow. Managing databases is an organisation-level act, so it needs an organisation-wide key. A key that can only see one database has no business listing or deleting others.
  • 2 · readingAn organisation-wide key may list databases, fetch one, and list branches with no further opt-in. Reads are safe, so they are not gated twice.
  • 3 · writingCreate, delete, reveal-connection-string and rotate additionally need both readOnly: false on the key (otherwise 403 read_only_key) and the organisation’s management_api feature flag switched on (otherwise 403 management_not_enabled).

No API key can switch the management_api flag on. A human being has to turn it on in the dashboard, and when it has never been touched it is off. That is the whole point of it: a leaked key cannot grant itself the right to delete your databases, because the right to grant that right does not exist over the API.

Two more refusals worth knowing. Anything that money would fix — a plan limit reached, a feature your plan excludes, a feature switched off — comes back as 402, with limit_reached, plan_excluded or feature_disabled. A member without the right permission gets 403 missing_permission. The split is deliberate: 402 means upgrade, 403 means ask an admin.

The rate limit here is 20 requests per 60 seconds, per resource, per key — much tighter than the SQL endpoint, because creating databases in a loop is either a mistake or an attack.

every route

  • GET /api/v1/databasesEvery database in the organisation. Not paginated.
  • POST /api/v1/databasesCreate one. 201 on success.
  • GET /api/v1/databases/{id}One database.
  • DELETE /api/v1/databases/{id}Delete one. Not idempotent.
  • GET /api/v1/databases/{id}/branchesEvery branch taken from that database. Not paginated.
  • POST /api/v1/databases/{id}/branchesTake a branch. 201 on success.
  • GET /api/v1/databases/{id}/connection-stringReveal the connection string.
  • POST /api/v1/databases/{id}/rotate-credentialIssue a new password and drop every open connection.

That is the complete list. There are no /api/v1 routes for API keys, history, undo, restore points, documents, members, billing or metrics. Those live in the dashboard and are served over tRPC, which is not a public interface. If you were about to guess a URL for one of them: it does not exist, and guessing will give you a 404 rather than a surprise.

creating a database

One field. The name is trimmed and must be 1 to 40 characters. A name already in use comes back as 409 name_taken.

bash

curl -sS https://briven.tech/api/v1/databases \
  -H "Authorization: Bearer $BRIVEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"notes"}'

response

{
  "id": "db_...",
  "name": "notes",
  "status": "provisioning",
  "failureReason": null,
  "hostKind": "shared",
  "createdAt": "2026-01-09T10:14:22.000Z"
}

Fetching one database uses the same object: GET /api/v1/databases/{id} answers 200 with it, 404 database_not_found if there is no such database, and 400 invalid_id if what you sent was never an id in the first place.

deleting a database

response

{ "deleted": true, "id": "db_...", "name": "notes" }

Delete is not idempotent, on purpose. A second DELETE of the same id answers 404, not 200. A retry loop that treats 404 as success will happily report having deleted a database it never saw — we would rather your script noticed.

One quiet detail worth knowing before you write a cleanup job: if the id you delete is a branch, the branch quota is refunded rather than the database quota. Deleting fifty branches does not buy you fifty databases.

branches

Listing branches returns the same database objects with four extra fields on each: parentDatabaseId, parentName, branchedAt and branchMethod. It is not paginated.

bash

curl -sS https://briven.tech/api/v1/databases/db_id/branches \
  -H "Authorization: Bearer $BRIVEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"pr-421"}'

response

{
  "branch": {
    "id": "db_...",
    "name": "pr-421",
    "parentDatabaseId": "db_...",
    "parentName": "notes",
    "branchedAt": "2026-01-09T10:16:03.000Z",
    "branchMethod": "sameClusterClone"
  },
  "fileCopyMethod": "clone",
  "cloneMilliseconds": 812,
  "terminatedBackends": 0
}

The branch name follows the same rule as a database name, 1 to 40 characters. Two refusals are specific to this route: 409 database_not_ready if the parent is not in a state that can be copied, and 409 name_taken. What a branch costs and what it is actually for is on branching.

connection strings and rotation

GET /api/v1/databases/{id}/connection-string answers { "connectionString": "postgresql://…" }. It is a write-gated route despite being a GET, because revealing a credential is not a read of public information.

bash

curl -sS -X POST \
  https://briven.tech/api/v1/databases/db_id/rotate-credential \
  -H "Authorization: Bearer $BRIVEN_API_KEY"

response

{
  "connectionString": "postgresql://...",
  "terminatedConnections": 2
}

Rotation takes no body and there is no grace period. The moment it returns, the old password is dead and every open connection has been dropped — terminatedConnections tells you how many. Anything still running on the old string will fail until you redeploy it with the new one, so rotate when you are ready to deploy, not before.

one route with no key at all

GET /api/v1/storage/<bucket>/<key…> serves public storage objects and takes no authentication, because a public object with a key requirement is not public. It answers a 302 redirect to a short-lived signed URL. Anything missing — and anything private — is a 404, so the route cannot be used to find out what private objects exist.

where to go next

The cli wraps every route on this page and keeps your key out of your shell history while doing it. To run statements against a database once you have made one, read sql over http or take a real connection with connecting.