Endpoint index
Every route the server mounts, in one table, with what it takes to reach it.
The paths assume the default basePath of /api; REBASE_BASE_PATH moves all
of them together. /health, /livez and /metrics sit outside it on purpose,
because an orchestrator probes /health and should not have to know the base
path. /health is also mounted under it, so /api/health answers the same
way rather than 404ing at the one moment someone is checking whether the server
is alive.
A gate — tooling/scripts/docs-verify/check-endpoint-index.mjs — compares this
table to the routes the source registers, so a new surface cannot be added
without appearing here.
| Gate | Means |
|---|---|
| none | Unauthenticated. Anyone who can reach the host can call it |
| session | A signed-in caller: an access token, or an API key scoped to the operation |
resource:action |
A caller holding that scope: an admin, a person whose role declares it, an API key created with it, or the service key |
| RLS | Authenticated, and then the database decides row by row — see Security Rules |
| dev | Mounted only outside production |
Generated per collection, so the paths carry your slugs rather than a fixed
list. :slug is a collection’s slug.
| Method | Path | Gate | More |
|---|---|---|---|
GET |
/api/data/:slug |
RLS | Querying |
POST |
/api/data/:slug |
RLS | REST API |
POST |
/api/data/:slug/query |
RLS | The listing, query parameters in the body — Queries too long for a URL |
GET |
/api/data/:slug/count |
RLS | Querying |
POST |
/api/data/:slug/count |
RLS | The count, query parameters in the body — Queries too long for a URL |
GET |
/api/data/:slug/aggregate |
RLS | REST API |
GET |
/api/data/:slug/:id |
RLS | REST API |
PATCH |
/api/data/:slug/:id |
RLS | REST API |
PUT |
/api/data/:slug/:id |
RLS | Deprecated alias of PATCH — same partial write, answers Deprecation: true |
DELETE |
/api/data/:slug/:id |
RLS | REST API |
POST |
/api/data/:slug/bulk |
RLS | Insert many rows, optionally upserting — REST API |
PATCH |
/api/data/:slug/bulk |
RLS | Update many rows by id — REST API |
POST |
/api/data/:slug/bulk/delete |
RLS | Delete many rows by id — REST API |
POST |
/api/data/_batch |
RLS | Write across collections in one transaction — Writing over REST |
GET |
/api/data/:slug/:id/history |
RLS | Entity History |
POST |
/api/data/:slug/:id/history/:historyId/revert |
RLS | Entity History |
Counting and aggregation are routes of their own, registered before /:id so
that aggregate is not read as an entity id. ?select= and ?groupBy= are
their parameters, and select is required on /aggregate.
Text search, vector search, relation inclusion and field selection are query
parameters on GET /api/data/:slug rather than routes — searchString,
vector_search, include, fields. See REST API.
A project that declares no collections and introspects none serves this prefix
as a single 404 NO_COLLECTIONS. See Backend only.
| Method | Path | Gate | More |
|---|---|---|---|
GET |
/api/auth/config |
none | What the login screen can offer: registration, password reset, magic link, email codes, guest sign-in, OAuth providers, and whether first-admin setup is pending |
POST |
/api/auth/register |
none | Authentication |
POST |
/api/auth/login |
none | Auth endpoints |
POST |
/api/auth/refresh |
none (a refresh token) | Auth endpoints |
POST |
/api/auth/logout |
session | Auth endpoints |
GET |
/api/auth/me |
session | Auth endpoints |
PATCH |
/api/auth/me |
session | Auth endpoints |
GET |
/api/auth/sessions |
session | Auth endpoints |
DELETE |
/api/auth/sessions |
session | Revokes every other session |
DELETE |
/api/auth/sessions/:id |
session | Revokes one |
POST |
/api/auth/forgot-password |
none | Authentication |
POST |
/api/auth/reset-password |
none (a reset token) | Authentication |
POST |
/api/auth/change-password |
session | Authentication |
POST |
/api/auth/change-email |
session | Mails a confirmation link to the new address — Changing an email address |
POST |
/api/auth/confirm-email-change |
none (a link token) | Moves the account onto the new address — Changing an email address |
POST |
/api/auth/send-verification |
session | Authentication |
GET |
/api/auth/verify-email |
none (a verification token) | Authentication |
POST |
/api/auth/verify-email |
none (a verification token, and a session or the password to keep it) | Email verification |
POST |
/api/auth/magic-link |
none | Authentication |
POST |
/api/auth/magic-link/verify |
none (a link token) | Authentication |
POST |
/api/auth/otp |
none | One-time codes by email |
POST |
/api/auth/otp/verify |
none (a code) | One-time codes by email |
POST |
/api/auth/anonymous |
none | Guest sessions. Off unless ALLOW_ANONYMOUS |
POST |
/api/auth/anonymous/link |
session (a guest) | Turns a guest into an account |
POST |
/api/auth/find-user |
session | Off unless AUTH_ALLOW_USER_LOOKUP — it is an enumeration surface |
POST |
/api/auth/:provider |
none | One per configured OAuth/OIDC provider |
POST |
/api/auth/link/:provider |
session | Links a provider to the signed-in account |
POST |
/api/auth/mfa/enroll |
session | MFA |
POST |
/api/auth/mfa/verify |
session | MFA |
GET |
/api/auth/mfa/factors |
session | MFA |
DELETE |
/api/auth/mfa/unenroll |
session | MFA |
POST |
/api/auth/mfa/recovery-codes |
session (aal2) |
MFA |
POST |
/api/auth/mfa/challenge |
none (a login in progress) | MFA |
POST |
/api/auth/mfa/challenge/verify |
none (a challenge id) | MFA |
GET |
/api/auth/scopes |
session | Every scope this backend knows, and the ones the caller holds — Roles and scopes |
GET |
/api/auth/keys |
session (an account) | The caller’s own personal keys |
POST |
/api/auth/keys |
session (an account) | The plaintext key is returned once. 403 PERSONAL_KEYS_DISABLED unless the users collection sets auth.personalKeys |
DELETE |
/api/auth/keys/:id |
session (an account) | Revokes one of the caller’s own keys |
GET |
/.well-known/jwks.json |
none | The public JWKS, when asymmetric signing is configured |
Everything under /api/admin needs a scope from the admin plane, and each
surface names its own: users:read lists accounts, cron:write triggers a job.
An admin holds all of them, and so does the service key. A person holds the ones
their roles declare, and an API key the ones it was created with. A key scoped to
a collection reaches none of this. See Roles and scopes.
| Method | Path | Gate | More |
|---|---|---|---|
POST |
/api/admin/bootstrap |
none, and only while no admin exists | Refused in production — see First User Bootstrap |
GET |
/api/admin/users |
users:read |
User management |
POST |
/api/admin/users |
users:write |
User management. Roles beyond the caller’s own are refused |
GET |
/api/admin/users/pick |
users:read, or write access to a collection with a userSelect field |
The user picker’s read: id, name, email and avatar only. A collection whose write rules are raw SQL does not count |
GET |
/api/admin/users/:uid |
users:read |
User management |
PUT |
/api/admin/users/:uid |
users:write |
Refused for an account that holds more than the caller |
DELETE |
/api/admin/users/:uid |
users:write |
Refused for an account that holds more than the caller |
POST |
/api/admin/users/:uid/reset-password |
users:write |
Issues a temporary password |
DELETE |
/api/admin/users/:uid/mfa |
users:write |
Removes the account’s second factors and recovery codes, ends its sessions |
GET |
/api/admin/guests |
users:read |
Guests, last seen longest ago first. ?lastSeenBefore= narrows them to what the delete below would take, and total counts them |
DELETE |
/api/admin/guests |
users:write |
Deletes one batch of the guests last seen before ?lastSeenBefore=, which is required. Answers { deleted, skipped, remaining }. Repeat while remaining is not 0, with ?offset= set to how many have been skipped. Accounts cannot be reached |
DELETE |
/api/admin/guests/:uid |
users:write |
Deletes one guest. An account is refused with NOT_ANONYMOUS |
GET |
/api/admin/roles |
users:read |
admin and the roles the project declares, with their scopes |
GET |
/api/admin/api-keys |
keys:read |
API keys. Never an API key |
POST |
/api/admin/api-keys |
keys:write |
The plaintext key is returned once, on creation |
GET |
/api/admin/api-keys/:id |
keys:read |
API keys |
PUT |
/api/admin/api-keys/:id |
keys:write |
API keys |
DELETE |
/api/admin/api-keys/:id |
keys:write |
API keys |
GET |
/api/admin/cron |
cron:read |
Cron Jobs |
GET |
/api/admin/cron/:id |
cron:read |
Cron Jobs |
PUT |
/api/admin/cron/:id |
cron:write |
Enable or disable a job |
GET |
/api/admin/cron/:id/logs |
cron:read |
Cron Jobs |
POST |
/api/admin/cron/:id/trigger |
cron:write |
Run a job now. ?async=1 answers 202 once it has started — Cron Jobs API |
GET |
/api/admin/backups |
backups:read |
Backup inventory |
GET |
/api/admin/backups/download |
backups:read |
Streams one backup. With ?token= from a download link, no header is needed, for that backup only |
POST |
/api/admin/backups/download-url |
backups:read |
A link to one backup, { key } → { url, token, expiresIn }, good for 60 seconds — Backups |
POST |
/api/admin/storage/orphans/scans |
database:write |
Starts a scan for the stored files no row names, ?storageId=&olderThanHours=, in the background. Answers 202 with the scan’s record: id, status (queued), and runner (worker when it is a storage.orphans job on the job queue, in-process when the server runs it itself). Its record is written into the source, under the reserved _rebase/maintenance/ |
GET |
/api/admin/storage/orphans/scans/:id |
database:read |
The scan’s record, ?storageId=. While it runs: progress (filesScanned, rowsScanned, collectionsScanned of collectionsTotal, unscanned). Once completed: result, the report the old GET /orphans answered. A run that stopped reporting for 10 minutes is answered as failed. 404 STORAGE_SCAN_NOT_FOUND when the source holds no such scan |
DELETE |
/api/admin/storage/orphans?scan=:id |
database:write |
Deletes a completed scan’s orphans in the background, 202. Each one is checked against every row and history entry again, and asked for again right before it is deleted. The outcome is the scan record’s deletion. 409 with STORAGE_SCAN_NOT_COMPLETE, STORAGE_SCAN_INCOMPLETE, STORAGE_SCAN_EXPIRED or STORAGE_SCAN_DELETION_STARTED |
GET |
/api/admin/storage/orphans |
database:read |
Deprecated: the scan inside the request, ?storageId=&olderThanHours=. Answers with Deprecation: true. Use POST /orphans/scans |
DELETE |
/api/admin/storage/orphans |
database:write |
Deprecated, without ?scan=: scans again and deletes inside the request. 409 STORAGE_SCAN_INCOMPLETE when a collection could not be read |
POST |
/api/admin/storage/move |
database:write |
Copies a folder to another source under the same keys, a page per call — what rebase storage move sends; 400 STORAGE_SAME_LOCATION for deleteSource between two sources that store in the same place |
POST |
/api/admin/storage/move/verify |
database:write |
Lists the source again after a move, with the same body, a page per call: { checked, notMoved: [{ key, reason }], cursor? } — every file still in the source with deleteSource, every file the target has no copy of without it. rebase storage move asks it when the move is done |
GET |
/api/admin/logs |
logs:read |
The recent log buffer |
GET |
/api/admin/logs/latest |
logs:read |
The most recent entries |
GET |
/api/admin/logs/stream |
logs:read |
Server-sent events |
GET |
/api/admin/rls-audit |
schema:read |
The scheduled audit’s latest result |
GET |
/api/admin/rls-simulator/options |
schema:read |
Roles, users’ rule roles and live service keys an administrator can simulate |
POST |
/api/admin/rls-simulator/preview |
schema:read |
Each collection as { role?, userId?, apiKeyId? } reaches it: visible rows against the caller’s, hidden by scope or RLS, masked fields. Administrator’s session only |
POST |
/api/admin/sql/csv |
database:write |
Streams one read-only query’s whole result as CSV — Studio’s “Download full result as CSV” |
GET |
/api/admin/branches/:name/diff |
database:write |
What a branch changed: schema, policies and rows, ?sampleLimit=&schemas=&maxRowsPerTable= |
POST |
/api/admin/branches/:name/merge |
database:write |
Writes the branch’s rows to its parent, { expectedFingerprint, onConflict?, skipBlocked? }; 409 when either side changed since the diff, or rows conflict |
GET |
/api/admin/schema/status |
schema:read |
Live schema editing |
POST |
/api/admin/schema/plan |
schema:read |
Plans a change; never applies one |
POST |
/api/admin/schema/apply |
schema:write |
A person only, unless REBASE_LIVE_SCHEMA_ALLOW_MACHINE_APPLY |
GET |
/api/admin/schema-editor/status |
schema:read |
Whether the editor is available, and the reason when it is not |
POST |
/api/admin/schema-editor/collection/save |
schema:write |
Studio — rewrites collection source |
POST |
/api/admin/schema-editor/collection/delete |
schema:write |
Studio |
POST |
/api/admin/schema-editor/property/save |
schema:write |
Studio |
POST |
/api/admin/schema-editor/property/delete |
schema:write |
Studio |
GET |
/api/admin/dev/emails |
dev + users:write |
Mail the development transport captured instead of sending |
DELETE |
/api/admin/dev/emails |
dev + users:write |
Empties the captured mailbox |
/api/admin/cron, /api/admin/logs and /api/admin/schema-editor are also
served at their pre-0.17 paths without the /admin segment. Those aliases are
for projects that have not moved; write new code against the canonical path.
Storage
Section titled “Storage”| Method | Path | Gate | More |
|---|---|---|---|
POST |
/api/storage/upload |
session + storageAuthorize |
Storage |
GET |
/api/storage/file/* |
session + storageAuthorize |
Storage |
DELETE |
/api/storage/file/* |
session + storageAuthorize |
Storage |
GET |
/api/storage/metadata/* |
session + storageAuthorize |
Storage |
POST |
/api/storage/metadata |
session + storageAuthorize, per key |
Storage: metadata for up to 100 objects |
GET |
/api/storage/list |
session + storageAuthorize |
Storage |
POST |
/api/storage/folder |
session + storageAuthorize |
Storage |
GET |
/api/storage/sources |
session | The named storage sources this backend serves |
OPTIONS |
/api/storage/tus |
none | Resumable uploads: the TUS versions and extensions this server supports |
POST |
/api/storage/tus |
session + storageAuthorize |
Resumable uploads: creation |
GET |
/api/storage/tus/:id |
the upload’s owner | Resumable uploads: offset |
PATCH |
/api/storage/tus/:id |
the upload’s owner | Resumable uploads: append |
DELETE |
/api/storage/tus/:id |
the upload’s owner | Resumable uploads: cancel |
A deployment with no storage configured serves this prefix as a 501 naming the
variable it needs, rather than 404ing as if the feature did not exist —
except GET /api/storage/sources, which answers 200 with an empty list.
Functions
Section titled “Functions”| Method | Path | Gate | More |
|---|---|---|---|
GET |
/api/functions |
session | The names of the functions served, and how many files failed to load |
| any | /api/functions/<name> |
whatever the function declares. An API key also needs functions:invoke |
Custom Functions |
One route per file under backend/functions/, so the paths come from your
project. GET /api/functions lists them only to a signed-in caller — a
session, an API key or the service key — and answers anyone else 401: an
inventory of a deployment’s custom endpoints is not public.
Meta and operations
Section titled “Meta and operations”| Method | Path | Gate | More |
|---|---|---|---|
GET |
/livez |
none | Liveness alone: is this process running. Does not touch the database, which is why it is the probe path a container should use — RUNTIME_LIVENESS_PATH |
GET |
/health, /api/health |
none | Liveness and readiness. Reports every configured data source, not only the default |
GET |
/api/docs |
none (schema:read when enableSwagger is false, the runtime’s default in production) |
The OpenAPI 3.0 document |
GET |
/api/swagger |
none | Swagger UI. Outside production only; REBASE_ENABLE_SWAGGER cannot turn it on in production. Not served when enableSwagger is false, in development either |
GET |
/api/meta/schema-version |
none | The schema hash this backend was built from and the runtime version, and nothing else |
GET |
/api/meta/contract |
schema:read |
The full collection contract, for rebase generate-sdk --from. 404 when no auth is configured |
GET |
/metrics |
REBASE_METRICS_TOKEN when set |
Prometheus metrics, when REBASE_METRICS=true |
GET |
/metrics/history |
REBASE_METRICS_TOKEN when set |
The recorded series behind the Studio charts. 501 on a runtime with no backend |
WebSocket connections arrive as an HTTP upgrade on the same server rather than at a path of their own — see Realtime.
MCP surface
Section titled “MCP surface”Mounted only when REBASE_MCP_ENABLED=true, which also requires
REBASE_PUBLIC_URL — see
Configuration. Off by
default: no REBASE_ROLE turns this on, because it hands project access to
third-party software and that is a decision for a person to make.
The .well-known documents sit at the origin, not under basePath: RFC 8414
and RFC 9728 define those paths relative to the origin, and a client fetches them
before it holds any token.
| Method | Path | Gate | More |
|---|---|---|---|
GET |
/.well-known/oauth-protected-resource |
none | RFC 9728 metadata naming this resource and its authorization server. Served at the path-suffixed form as well |
GET |
/.well-known/oauth-authorization-server |
none | RFC 8414 metadata: the endpoints, grant types and PKCE methods this deployment supports |
POST |
/mcp |
OAuth bearer, or an API key | The MCP protocol endpoint. Acts as the signed-in user (or as whoever the key acts as), so every read and write is subject to the same RLS. The data:* scopes decide which tools are offered |
GET |
/mcp |
OAuth bearer, or an API key | Answers 405 with Allow: POST, DELETE: this server opens no server-initiated stream. The token is checked first, so a missing or stale one gets the 401 challenge instead |
DELETE |
/mcp |
none | Answers 204. The endpoint keeps no session, so there is nothing to end |
POST |
/api/oauth/register |
rate-limited | RFC 7591 dynamic client registration. Refused when REBASE_MCP_OPEN_REGISTRATION=false |
GET |
/api/oauth/authorize |
session | The consent screen a client is redirected to |
POST |
/api/oauth/authorize/decision |
session | The person’s answer to it — approve or deny |
POST |
/api/oauth/token |
client credentials + PKCE | Exchanges an authorization code, or refreshes |
POST |
/api/oauth/revoke |
client credentials | RFC 7009 token revocation |
GET |
/api/oauth/grants |
session | Which clients this user has approved |
DELETE |
/api/oauth/grants/:clientId |
session | Withdraws one, so a person can undo a consent without an admin |
Related
Section titled “Related”- REST API — the data routes in full: filters, sorting, pagination, errors
- Auth endpoints — request and response shapes for the auth table above
- Environment & Configuration — the variables that decide which of these are mounted