Programmatic surface.
The endpoints we'll keep stable across versions. Auth is the same session cookie the UI uses - sign in with curl + browser dev tools, or use one of the documented `Authorization` headers below.
Authentication
All endpoints below require an authenticated browser session via the authjs.session-token (or __Secure-authjs.session-token in production) cookie. The simplest way to use them from a script:
- Sign in via the browser at /signin.
- Copy the session cookie value from the dev tools.
- Send it with each request via
Cookie: authjs.session-token=….
The session cookie is httpOnly, so it cannot be accessed from page JavaScript - that's by design. For scripts and CI, mint a personal API token instead (see the public API section below): it authenticates with a plain Authorization: Bearer header and needs no cookie.
CSRF
Any POST / PUT / PATCH / DELETE request must either have no Origin header (server-to-server) or carry an Origin that matches the deployment host. Cross-site origins are rejected with 403 forbidden at the middleware. Webhooks (/api/webhooks/*) are exempt because they authenticate via HMAC signature instead.
Account
POST /api/account/forgot-password
Trigger a password-reset email. Body { email: string }. Always returns 200 { ok: true, configured: boolean }enumeration-resistant. configured: false means email isn't wired on this deployment.
POST /api/account/reset-password
Consume a reset token. Body { token: string, password: string }. 200 on success, 410 when the token has expired, 409 when the token has already been consumed, 404 when unknown.
POST /api/account/change-password
Rotate password while signed in. Body { currentPassword, newPassword }. Returns 200 on success, 400 when the current password doesn't match, 409 for OAuth-only accounts (no password to change).
POST /api/account/verify-email/start
Re-send the email-verification link to the current user. Body empty. 200.
POST /api/account/verify-email/confirm
Consume a verification token. Body { token: string }. 200 / 410 / 409 / 404.
GET /api/account/export
GDPR data portability. Returns the signed-in user's full dataset as a single JSON file with Content-Disposition: attachment. Encrypted columns (Supabase keys, Postgres URL, TOTP secret) are excluded.
POST /api/account/2fa/{setup,enable,verify,disable}
The four-step 2FA lifecycle. See /settings/account/2fa for the wired UI; the API contracts are: setup returns a fresh secret + QR data URL; enable takes { secret, code } and persists; verify takes { code, recovery? } and sets the MFA cookie; disable takes { password } and clears.
Billing
POST /api/billing/checkout
Kick off a Dodo hosted checkout. Body empty. Returns { checkoutUrl: string }: the client navigates to it. 409 when the user already has an active subscription; 503 when billing isn't configured.
POST /api/billing/portal
Mint a Dodo customer-portal URL. Returns { url: string }. 404 when the user has no Dodo customer record yet.
POST /api/webhooks/dodo
Inbound Standard Webhooks endpoint. Verified by HMAC-SHA256 over `${webhook-id}.${webhook-timestamp}.${rawBody}` using DODO_WEBHOOK_SECRET. 5-minute replay tolerance. Idempotent via billing_event.webhook_id + applied_at.
Operational
GET /api/health
Liveness + readiness. Returns 200 when the database is reachable, 503 when not. Body shape:
{
"status": "ok" | "degraded",
"db": boolean,
"email": boolean,
"billing": boolean,
"observability": boolean,
"version": "3.x.y"
}POST /api/cron/retention
Manual trigger for audit-log + sentry-finding retention. Requires Authorization: Bearer <CRON_SECRET>. Returns the row counts pruned per table. Designed for cron-job.org / Coolify cron / GitHub Actions - not for in-app calls.
Public API v1 (personal API tokens)
Create a token under /settings/api-tokens. The plaintext (sbp_…) is shown once; only a SHA-256 is stored. Tokens are read-only, carry exactly the access of the user who minted them (every connection they own or are a member of), can expire, and can be revoked at any time. Rate limit: 240 requests / minute per token. Every route lives under /api/public/v1 and answers JSON.
curl -H "Authorization: Bearer sbp_…" https://suparbase.com/api/public/v1/connectionsGET /api/public/v1/me
The token's owner: { user: { id, email, name }, tokenId, scope: "read" }.
GET /api/public/v1/connections
Every connection the owner can access: { connections: [{ id, name, hostname, url, keyRole, environment, myRole, hasPostgresUrl, createdAt, lastUsedAt }] }. No secrets, ever.
GET /api/public/v1/connections/:id/schema
Live introspected schema in the compact snapshot shape: { hostname, introspectedAt, tables: [{ schema, name, kind, primaryKey, columns: [{ name, pgType, nullable, defaultValue, fk? }] }] }.
GET /api/public/v1/connections/:id/activity
Audit timeline, newest first. Query params verb=insert|update|delete, table, limit (max 200) and before (ISO timestamp) for keyset pagination; the response carries nextBefore to feed back in.
GET /api/public/v1/connections/:id/sentry/findings
Agent Sentry findings (all statuses) plus the most recent scans: { findings, scans }.
POST /api/public/v1/connections/:id/sql
Body { sql: string, statementTimeoutMs?: number }. Runs inside SET TRANSACTION READ ONLY and always rolls back, so a token can never write. Needs the connection's Direct Postgres URL. Returns { columns, rows, rowCount, truncated, elapsedMs }(capped at 1,000 rows).
Errors
401 missing / unknown / revoked / expired token, 404 connection not visible to the owner, 429 with Retry-After when the per-token budget is exhausted. Every error body is { category, message }.
POST /api/cron/{reports,watches,sentry,sync}
Operator cron routes, same Bearer <CRON_SECRET> contract as retention. sentry (v3.20) re-scans every connection whose owner enabled a scheduled cadence.
What's NOT public yet
The following exist and work, but their shape may change without notice - script against them at your own risk:
/api/v/<id>/*: the encrypted proxy. The contract follows PostgREST's under the hood, but our auth + filtering layer wraps it./api/connections/<id>/sentry/*: Agent Sentry scans + findings + quarantines./api/connections/<id>/sessions/*: agent sessions + one-click undo./api/connections/<id>/{widgets,actions,members}/*tied closely to the UI; expect shape changes./api/ai/*: AI chat conversations + analysis.
If you need a stable contract on any of these, let us know via our contact form - we'll publish it here once we know it's worth keeping stable.