sbarbase
Contents

API

Routes served by the loopback server (lab/upstream-server.ts). Paths starting with /management/ are the management API, /v1/ and /mcp are for agents, and everything else is the application gateway. Read from the code on 2026-10-19; the code is authoritative:

The console and its static assets are served beside these routes by lab/ui-static.ts. The loopback server refuses any Host other than 127.0.0.1 or localhost; public exposure goes through a TLS proxy (see server deployment).

Management login

Proxied to the dedicated management Auth realm, with the publishable routing key sb_publishable_sbarbase_local_management. Point the Supabase SDK at <base URL>/management.

Method Path Purpose
POST /management/auth/v1/token Log in (password grant) or refresh
GET /management/auth/v1/user Current operator
POST /management/auth/v1/logout Log out
GET /management/auth/v1/settings Auth settings
GET, PATCH /management/auth/v1/policy Whether the installation requires MFA, and whether the caller still needs a code. Only an installation owner who verified an authenticator code in the last five minutes may change it, with {"mfa_required": bool}
POST /management/auth/v1/factors Enroll an authenticator (TOTP) for the signed-in account
POST /management/auth/v1/factors/{id}/challenge, /management/auth/v1/factors/{id}/verify Ask for and check a code
DELETE /management/auth/v1/factors/{id} Remove an authenticator

Any other /management/auth/... path is 404; a wrong method is 405. Sign-in attempts are rate limited and answer 429 past the limit.

Management API

All routes need Authorization: Bearer <management access token>. The actor comes from that token, never from the body. IDs are UUIDs. Errors: 400 invalid input, 401 no valid token, 403 not allowed (also for an unknown ID), 404 unknown route, 405 wrong method, 409 conflict (environment not ready, name already used in that client or project, environment limit reached, not retryable, still holds projects or environments, provisioning or a service still active); nothing was changed, 503 identity check unavailable, 500 sanitized internal error.

Method Path Who Result
GET /management/v1/organizations any operator The caller's clients (organizations) with ID, name and role, and operator: true when the caller may create clients
POST /management/v1/organizations owner or admin of the client created at bootstrap Body {"name": "..."}. 201 with {id}; the caller becomes its owner
PATCH /management/v1/organizations/{id} owner Body {"name": "..."}. Renames the client
DELETE /management/v1/organizations/{id} owner Deletes an empty client with its memberships and pending invitations. 409 while it has projects, and always for the client created at bootstrap
GET /management/v1/organizations/{id}/members owner, admin Members of that client with their role
PUT /management/v1/organizations/{id}/members/{member} owner Body `{"role": "owner"
DELETE /management/v1/organizations/{id}/members/{member} owner Removes the member; their management and Studio access ends at the next request. 409 for the last owner
GET /management/v1/organizations/{id}/invitations owner, admin Pending invitations: email, role, inviter, expiry; never the token
POST /management/v1/organizations/{id}/invitations owner, admin Body {"email": "...", "role": "..."}; an admin cannot invite an owner. 201 with {id, token, expires_at}; the token is shown once and lasts 7 days
DELETE /management/v1/organizations/{id}/invitations/{invitation} owner, admin Cancels a pending invitation
POST /management/invitations/redeem anyone with the token Body {"token": "...", "password": "..."} creates the account (password of 12 characters or more) and joins; with a session for the invited email, {"token": "..."} joins. 400 This invitation is not valid for any unknown, used, cancelled or expired token; 409 when an account exists and must sign in first; 429 after 20 failures in a minute
GET /management/v1/organizations/{id}/projects member Projects of that client
POST /management/v1/organizations/{id}/projects owner, admin Body {"name": "..."}. 201 with {id, state: "metadata_only"}
PATCH /management/v1/projects/{id} owner, admin Body {"name": "..."}. 409 when the name is used in that client
DELETE /management/v1/projects/{id} owner Deletes a project that has no environment; 409 otherwise
POST /management/v1/projects/{id}/move owner of both clients Body {"organization": "<id>"}. Moves the project; its ids and runtimes stay. Queued provisioning is cancelled (retry it in the new client) and every API key of its environments is revoked. 200 with {organization, cancelled, revoked}; 409 on a name clash in the destination or while provisioning runs
GET /management/v1/projects/{id}/environments member Environments of that project
POST /management/v1/projects/{id}/environments owner, admin Body {"name": "..."}. 202 with {id, state: "queued"}; the worker provisions it
PATCH /management/v1/environments/{id} owner, admin Body {"name": "..."}. 409 when the name is used in that project
DELETE /management/v1/environments/{id} owner Removes the environment from management and revokes its keys; from then on the gateway answers 401 for its runtime. Its database, containers and files stay on the server, retained for seven days. 200 {deleted, revoked, retentionUntil}. 409 while provisioning is queued or running, while Studio, Realtime, Edge Functions, database access, a sign-in change, a signing key rotation, or a paused or moved routing is on or in progress, and when the exact list of the environment's resources was never recorded, which on an ordinary installation is always (see below)
GET /management/v1/environments/{id}/lifecycle owner, admin The deletion record: state (active, deleting, deleted, restoring, purging, purged), retain_until, the resources and what was done to each
POST /management/v1/environments/{id}/restore owner Body {"operation": "<new uuid>"}. Brings a deleted environment back while it is retained. 202; 409 once retention has expired
POST /management/v1/environments/{id}/purge owner who is an installation operator Body {"operation": "<new uuid>"}. Removes a deleted environment's resources for good, only after retention and only with a complete recovery proof. 202
POST /management/v1/environments/{id}/resume owner Body {"operation": "<uuid>"} of the deletion, restore or purge under way. Renews its authorization so it can continue
GET /management/v1/projects/{id}/retained-environments owner, admin The project's environments that are deleted or on their way
POST /management/v1/relink installation operator Body {"runtime": "e_...", "ownership": {...}}, the ownership a backup's manifest records. Recreates that client, project and environment here with the same ids and runtime, and queues provisioning; sbarbase relink calls it (backup and restore). 409 for any conflict: a client with that name but another id, an id that belongs to something else here, a runtime in use or deleted here
GET /management/v1/environments/{id}/provision member Provisioning state, attempt and failure if any
POST /management/v1/environments/{id}/retry owner, admin No body. 202 {state: "queued"} for a failed or cancelled environment; 409 otherwise or at the environment limit
GET /management/v1/environments/{id}/connection member, when ready {environment, apiPath: "/<runtime>", services}
GET /management/v1/environments/{id}/keys owner, admin Key metadata, and token for an active publishable key created since 0.5.0; never a secret key
POST /management/v1/environments/{id}/keys owner, admin Optional {type: "publishable"|"secret", name}; no body makes a publishable key named default. 201 {id, token, type, name}; a secret key is shown only here (API keys)
GET /management/v1/access-tokens signed-in person The caller's own access tokens, never the tokens themselves
POST /management/v1/access-tokens signed-in person {name, scope?: {kind: "all"|"organization"|"project", id?}, read_only?, days?} (1 to 365, 90 by default). 201 {data, token}, the token shown once (AI agents)
DELETE /management/v1/access-tokens/{id} signed-in person Revokes one of the caller's tokens
POST /management/v1/access-tokens/revoke-all signed-in person Revokes every token of the caller
DELETE /management/v1/environments/{id}/keys/{keyId} owner, admin 200 {revoked: true}, or 404 if not an active key of this environment
GET /management/v1/environments/{id}/metrics member, when ready Last hour at the gateway: window (requests, clientErrors, serverErrors, p50 and p95 in ms, services counts), perMinute (60 rows), since, and services (memory and processor use of Auth, REST, Realtime and Edge Functions). In memory; empty after a restart (logs and metrics)
GET /management/v1/environments/{id}/logs?source=requests|auth|rest|storage|realtime|functions&lines=1-1000&errors=1 owner, admin, when ready requests: the last gateway requests, newest first, without query strings. Other sources: lines, the service's last lines, oldest first, with keys, tokens and passwords replaced by [redacted]; Storage lines only when they name this environment. 503 when Docker cannot be reached
GET /management/v1/environments/{id}/functions owner, admin, when ready Edge Functions state and desired (as for Realtime), the deployed functions with verify_jwt, updated_at, size and path, and the names of the secrets (never their values)
PUT /management/v1/environments/{id}/functions owner, admin, when ready {"enabled": true|false}. 202; the supervisor starts or stops the environment's runtime
PUT /management/v1/environments/{id}/functions/{name} owner, admin, when ready Deploy: {"files": {"index.ts": "..."}, "shared"?: {...}, "verify_jwt"?: bool}, text files at relative paths, at most 500 files and 10 MiB. 201; the first deploy turns Edge Functions on
DELETE /management/v1/environments/{id}/functions/{name} owner, admin, when ready Removes the function; 404 if there is none
PUT /management/v1/environments/{id}/function-secrets owner, admin, when ready {"secrets": {"NAME": "value" | null}}; null removes. Names are A-Z, digits and _, not starting SUPABASE_ or SB_. Answers the names only
GET /management/v1/environments/{id}/database owner, admin, when ready Direct database access state and desired, the connection (host, port, user, database) and a url with a password placeholder
PUT /management/v1/environments/{id}/database owner, admin, when ready {"enabled": true|false}. 202; turning it on answers a new password and full url once
POST /management/v1/environments/{id}/database/password owner, admin, when ready A new password, answered once; the old one stops working. 409 while access is off
GET /management/v1/environments/{id}/signing-key owner, admin, when ready The JWT signing key's state (never, pending, done, failed), failure and rotatedAt; never the key
POST /management/v1/environments/{id}/signing-key/rotate owner, admin, when ready A new signing key (signing key). 202; 409 while a rotation is pending
GET /management/v1/environments/{id}/mail member Non-secret mail state of the environment; no credential field
GET /management/v1/notifications owner or admin of any client Undelivered count and recent operator events of the caller's own clients only. Events that belong to no client (installation start, worker restarts) go to owners and admins of the client created at bootstrap
GET /management/v1/organizations/{id}/audit owner, admin What happened in that client, newest first (up to 100): time, actor, action, subject name and a short detail. A project moved in from another client shows only what happened since it arrived
GET /management/v1/environments/{id}/share member The environment's guaranteed gateway share, the default and the ceiling; installation operators also get total and allocated
PUT /management/v1/environments/{id}/share installation operator Body {"share": n}, 1 to 24. 409 when the shares of all ready environments would exceed 64. Applies at the next request
GET /management/v1/environments/{id}/realtime owner, admin, when ready Realtime state and desired (Realtime)
PUT /management/v1/environments/{id}/realtime owner, admin, when ready {"enabled": true|false}. 202; the supervisor starts or stops it. 409 while a change is in progress
GET /management/v1/environments/{id}/sign-in owner, admin, when ready Sign-in settings without provider secrets, their state and the callback_url to give OAuth providers (sign-in)
PUT /management/v1/environments/{id}/sign-in owner, admin, when ready New settings. 202; the supervisor recreates only that environment's Auth. 400 names the setting to check
GET /management/v1/environments/{id}/studio owner, admin, when ready Studio state and whether a public Studio address is set (Studio)
POST, DELETE /management/v1/environments/{id}/studio owner, admin, when ready No body. Start or stop Studio; 202
POST /management/v1/environments/{id}/studio/session owner, admin, when ready No body. 201 {host, public, path}: the Studio address and a one-time entry ticket. 409 while Studio is not running
GET, POST, PUT /management/v1/environments/{id}/deployment-setup installation operator, when ready A prepared public deployment plan (address, DNS records, TLS, site URL, redirects). POST previews, PUT with If-Match: <revision> saves (202). It is preparation only: nothing is applied to the runtime
GET /management/v1/server installation operator The server snapshot the console shows: CPU cores, load, uptime, memory, swap and disk (console design). Not cached
GET /management/v1/notifications/telegram installation operator state (off, pending or linked), the token's last four characters and owner; only an owner sees the pending link
PUT /management/v1/notifications/telegram installation owner Body {"token": "..."}. Checks the bot token with Telegram and keeps it (configuration)
POST /management/v1/notifications/telegram/link, /management/v1/notifications/telegram/test installation owner No body. Bind the chat that opened the one-time link, or send a test message
DELETE /management/v1/notifications/telegram installation owner Disconnects and forgets the token

Request bodies accept exactly the fields listed for the route, at most 4 KiB, read within five seconds. Key and connection routes and every DELETE refuse any body. Responses are not cacheable. A move revokes API keys but does not rotate the environments' JWT signing key or direct database password; rotate them (signing key) when people of the old client must lose every kind of access. Deleting an environment does not free its slot in the worker's environment limit, because its runtime is kept. On an ordinary installation, deleting an environment always answers 409 with "Exact ownership inventory unavailable" and changes nothing. Provisioning does not record the exact list of an environment's resources, and none could be recorded honestly: every environment shares the PostgreSQL cluster and the Storage process, and nothing yet proves that a database and its Storage files are written by that environment alone, so the lifecycle refuses any list that names them. Only the lifecycle probes reach a successful delete, on disposable fixtures. Restore and purge start from a deleted environment, so they cannot be reached either.

Agents: the Supabase-compatible API and MCP

With an access token as Authorization: Bearer sbp_...: /v1/... answers the part of Supabase's Management API that agents and tools use, and POST /mcp runs Supabase's own MCP server against it. What each does here and its limits are in AI agents and the API. An access token is refused by every /management/ route. A project {ref} is one environment, 20 lowercase letters.

Method Path
GET /v1/organizations, /v1/organizations/{id}
GET, POST /v1/projects
GET /v1/projects/{ref}, /v1/projects/{ref}/health, /v1/projects/{ref}/api-keys/legacy
GET, POST /v1/projects/{ref}/api-keys
DELETE /v1/projects/{ref}/api-keys/{id}
POST /v1/projects/{ref}/database/query
GET, POST /v1/projects/{ref}/database/migrations
GET /v1/projects/{ref}/advisors/security, /v1/projects/{ref}/advisors/performance (always empty), /v1/projects/{ref}/analytics/endpoints/logs
GET, PUT, PATCH /v1/projects/{ref}/functions
POST /v1/projects/{ref}/functions/deploy
GET, DELETE /v1/projects/{ref}/functions/{name}; GET /v1/projects/{ref}/functions/{name}/body
GET, POST, DELETE /v1/projects/{ref}/secrets
GET /v1/projects/{ref}/config/storage; /v1/projects/{ref}/config/database/pooler and /v1/projects/{ref}/branches answer an empty list
POST /mcp

Deleting, pausing or restoring a project and generating types answer 501 with what to do instead; any other /v1 path answers 404.

Updates

The update channel's routes (upgrades), in src/control/http.ts and src/control/updates.ts. Only the installation operator (an owner or admin of the client created at bootstrap) may call them; anyone else gets 403. The console only reads and asks: the supervisor carries out each request after judging it again, and publishes its verdicts in .lab/upgrades/current.json.

Method Path Result
GET /management/v1/updates {data} with current (version, commit), available (the release on offer: version, tag, commit, class of safe, attended, rebuild or manual, signed, reasons, notes in en and ar, changes) or null, refusals, skipped and newest (newer releases the check passed over), checkedAt, checkError, settings, timezone (name, offset), last (the last upgrade and its phase), request (the one under way, else the last finished), install (possible, reason, acknowledgement, or null when nothing is on offer) and canRollback
PUT /management/v1/updates/settings Body {"check": bool, "automatic": bool, "window": {"start": "HH:MM", "end": "HH:MM"}}, 24-hour server local time. 200 with the saved data and the timezone. 400 with a sentence for invalid settings, and when automatic is on without check
POST /management/v1/updates/check No body. 202 {state: "requested"}: the supervisor runs one check
POST /management/v1/updates/apply Body {"version": "X.Y.Z", "acknowledged"?: bool}. 202 {state: "requested"}. 400 without a valid version. 409 with a sentence when the supervisor has not judged the release yet, nothing is on offer, the version is not the one on offer, its verdict says it cannot be installed now (with the verdict's reason), or it is an attended release and acknowledged is not true
POST /management/v1/updates/rollback No body. 202 {state: "requested"}, or 409 with the reason when the last upgrade cannot be rolled back

Every request route answers 409 with "Another update request is still in progress" while another request holds the slot. While a new version waits for its health checks, reads, the management sign-in, check, settings and rollback pass; every other management change answers 409 with a sentence saying changes are paused, and application routes answer 503 with Retry-After: 5. The supervisor's own health probe passes the hold on REST and Auth only, with a per-start token and never with x-forwarded-* headers (src/gateway/hold-bypass.ts).

Loopback health

GET /health (or HEAD) on the loopback listener, for 127.0.0.1 or localhost only, needs no token. It answers 200 {"status": "ok", "held": bool} once the process serves and both the control catalog and the key store read, and 503 {"status": "unavailable"} otherwise; any other method is 405. held says whether application traffic is held for an update. The supervisor's health round after an update starts with this route (src/http/health.ts).

Application gateway

{METHOD} /{runtime}/auth/v1/{path}
{METHOD} /{runtime}/rest/v1/{path}
{METHOD} /{runtime}/storage/v1/{path}

{runtime} is the environment's runtime ID returned as apiPath above. Point supabase-js at <base URL>/{runtime} with a publishable key.

Status Meaning
401 Missing or invalid API key, a malformed Authorization header, or an environment that was deleted
404 Unknown environment, route or unconfigured service
408 Request cancelled by the client
413 Body over 1 MiB, or an upload over the upload limit
429 This environment is at its share and cannot borrow more right now, or at its ceiling; retry later
503 Environment in maintenance, server-wide request limit reached, or routing unavailable; retry-after: 1 where retrying helps
504 Upstream deadline exceeded

Admission limits are per gateway process and have no queue.

Realtime

When an environment has Realtime turned on (Realtime):

Route Meaning
GET /{runtime}/realtime/v1/websocket?apikey=<publishable key>&vsn=1.0.0 with Upgrade: websocket The Realtime socket supabase-js opens. The key is checked first; Realtime receives the environment's anon token instead, or a service_role token for a secret key sent from a server
POST /{runtime}/realtime/v1/api/broadcast Broadcast from a server, with the apikey header

With Realtime off, the socket answers 404. A wrong or revoked key answers 401. Every other Realtime path answers 404.

Edge Functions

When an environment has functions deployed (Edge Functions):

Route Meaning
ANY /{runtime}/functions/v1/{name}[/...] Runs the function. With an apikey, the key is checked as for every service; without one, only a function deployed with verify_jwt off runs, and the caller's own headers reach it

A name is letters, digits, - and _, at most 64, starting with a letter or digit; anything else, an unknown function or Edge Functions turned off answers 404. A function that checks JWTs answers 401 to a missing or invalid token. The deadline is 150 seconds; bodies stream up to the upload limit.

Edit on GitHub