Every endpoint the backend serves. The live, machine-readable version is at
GET /api/v1/openapi.json — and if you call it with an admin token, you get the spec
typed from your own collections rather than the generic one.
Two token audiences exist and they reject each other: a control-plane token cannot be used
on data routes, and a data token cannot be used on control routes.
| Label |
Meaning |
| public |
No credentials. |
| end-user JWT |
Authorization: Bearer <jwt>, audience project. The pid claim selects the project. |
| data token |
An end-user JWT or an API key. API-key callers also pass a scope gate. |
| scope:X |
The API key must carry scope read, write, admin, storage or functions. admin implies all. JWT callers skip the scope gate and are governed by RBAC plus collection policies. |
| control token |
Authorization: Bearer <jwt>, audience control — a platform user. |
| operator |
A control token whose platform role is super-admin. |
Project selection. Authenticated routes use the token’s pid; passing an
x-groveback-project header that disagrees returns 400 PROJECT_MISMATCH. Unauthenticated
auth and storage routes resolve the project from the x-groveback-project header, then
?project=, then the deployment default — 400 MISSING_PROJECT if the deployment serves
several projects and you named none.
| Method |
Path |
Description |
Auth |
| GET |
/api/v1/health |
Liveness, plus version, commit, chartVersion. |
public |
| GET |
/api/v1/openapi.json |
OpenAPI 3.1 spec; upgrades to your typed per-project spec when a valid admin token is present. |
public (optional admin token) |
| GET |
/api/v1/projects/:pid/openapi.json |
Typed spec for one project; pid must match the token. |
scope:admin |
| GET |
/docs |
Swagger UI. |
public |
| GET |
/graphiql |
GraphiQL IDE. |
public |
| GET |
/p/:pid/docs · /p/:pid/graphiql |
Per-project versions of the two above. |
public page |
| GET |
/dashboard, /dashboard/* |
The admin SPA. |
public page (its API calls are authed) |
| GET |
/auth/login |
Hosted login page (?project=&redirect=). The redirect origin must be allow-listed. |
public |
| GET |
/reset-password |
Hosted password-reset form (?token=&project=). |
public |
| GET |
/examples/oauth |
Static OAuth demo page. |
public |
Anything outside /api/v1/** that is not a known page returns 404 NOT_FOUND.
Handled under /api/v1/auth/*. register, login, refresh, verify-email,
password-reset and mfa are rate-limited per ip:project:action (default: 10 per 60s),
returning 429 RATE_LIMITED with a retry-after header.
| Method |
Path |
Description |
Auth |
| POST |
/api/v1/auth/register |
Create an end user (email, password, optional name) → 201 {user}. |
public |
| POST |
/api/v1/auth/login |
Password login → tokens, or {mfaRequired, mfaToken} when TOTP is on. |
public |
| POST |
/api/v1/auth/refresh |
Exchange refreshToken for a new access token. |
public |
| POST |
/api/v1/auth/logout |
Revoke the presented refreshToken → 204. |
public |
| POST |
/api/v1/auth/verify-email/request |
Send a verification email. Always 204 — it never reveals whether the address exists. |
public |
| POST |
/api/v1/auth/verify-email |
Consume the token → 204. |
public |
| POST |
/api/v1/auth/password-reset/request |
Send a reset email. Always 204. |
public |
| POST |
/api/v1/auth/password-reset |
Reset with token + new password → 204. |
public |
| POST |
/api/v1/auth/mfa/verify |
Second factor for a pending login (mfaToken, code) → tokens. |
public |
| POST |
/api/v1/auth/mfa/totp/enroll |
Begin TOTP enrollment → secret and otpauth URL. |
end-user JWT |
| POST |
/api/v1/auth/mfa/totp/activate |
Confirm with code → recovery codes. |
end-user JWT |
| POST |
/api/v1/auth/mfa/totp/disable |
Turn TOTP off with code → 204. |
end-user JWT |
| GET |
/api/v1/auth/sessions |
List the caller’s sessions (token hashes stripped). |
end-user JWT |
| DELETE |
/api/v1/auth/sessions/:id |
Revoke one session → 204. |
end-user JWT |
| Method |
Path |
Description |
Auth |
| GET |
/api/v1/auth/oauth/:provider/start |
302 to the provider (google or github). ?redirect= must be an absolute URL whose origin is allow-listed; the state is HMAC-signed and carries the project id. |
public |
| GET |
/api/v1/auth/oauth/:provider/callback |
Exchanges ?code, then 302s back to redirect with #mb_refresh=…, #mb_mfa=… or #mb_error=<reason>. |
public |
The callback never returns an error status — failures come back in the URL fragment. See
social login.
/api/v1/:collection[/:id]. Reserved names (__*, system.*, and the system collection
list) are rejected as 404 NOT_FOUND rather than 403, so the API does not disclose them.
| Method |
Path |
Description |
Auth |
| GET |
/api/v1/:collection |
List documents, policy-filtered → {documents: [...]}. |
scope:read |
| POST |
/api/v1/:collection |
Create → 201 {document}. |
scope:write |
| GET |
/api/v1/:collection/:id |
Fetch one → {document}. |
scope:read |
| PATCH |
/api/v1/:collection/:id |
Partial update. A body whose keys start with $ passes through as Mongo operators; otherwise it is wrapped in $set → 204. |
scope:write |
| PUT |
/api/v1/:collection/:id |
Full replace. A body containing $ keys is rejected with 400 INVALID_BODY → 204. |
scope:write |
| DELETE |
/api/v1/:collection/:id |
Delete → 204. |
scope:write |
| POST |
/api/v1/:collection/search |
Vector similarity search → {results}. POST-only, so GET /:collection/search still resolves a document whose id is search. Scoped as read. |
scope:read |
For the query parameters these accept, see query syntax.
| Method |
Path |
Description |
Auth |
| POST |
/api/v1/graphql |
Execute against the project’s schema. The required scope is derived from the operation — mutation needs write, query needs read. Collection policies apply on top. Always returns 200 with a {data, errors} envelope. |
scope:read/write |
| Path |
Description |
Auth |
GET /api/v1/realtime (upgrade) |
WebSocket. A non-upgrade request gets 426 WebSocket upgrade required. |
the first message must be {type:"auth", token} |
Protocol and message shapes are in the realtime guide.
Everything under /api/v1/admin/* requires a data token with scope:admin, and then a
per-service permission check (collections:manage, roles:manage, config:manage, …).
| Method |
Path |
Description |
| GET |
/api/v1/admin/collections |
List collection names. |
| POST |
/api/v1/admin/collections |
Create a collection (name) → 201, empty body. |
| DELETE |
/api/v1/admin/collections/:name |
Drop the collection → 204. |
| POST |
/api/v1/admin/collections/:name/rename |
Rename to to → 204. |
| GET · PUT · DELETE |
/api/v1/admin/collections/:name/schema |
Read, replace or remove the JSON Schema. |
| GET |
/api/v1/admin/collections/:name/policy |
{policy, source} — source is own, inherited or default. |
| PUT · DELETE |
/api/v1/admin/collections/:name/policy |
Set or clear access rules → 204. Audited. |
| GET · POST |
/api/v1/admin/collections/:name/indexes |
List indexes, or create one (keys object required; optional unique, name) → 201 {name}. |
| DELETE |
/api/v1/admin/collections/:name/indexes/:indexName |
Drop an index → 204. |
These are admin-gated and deliberately skip the policy engine — this is the dashboard’s data
browser.
| Method |
Path |
Description |
| GET |
/api/v1/admin/collections/:name/documents |
List → {documents, total}. Supports filter, limit, skip, expand. |
| POST |
/api/v1/admin/collections/:name/documents |
Create → 201 {document}. |
| GET |
/api/v1/admin/collections/:name/documents/:id |
Fetch one; supports expand. |
| PATCH · PUT |
/api/v1/admin/collections/:name/documents/:id |
Partial ($set, operators pass through) or full replace → 200 {document}. |
| DELETE |
/api/v1/admin/collections/:name/documents/:id |
Delete → 204. |
| Method |
Path |
Description |
| GET |
/api/v1/admin/vector-configs |
Every vector config in the project → {configs}. |
| GET · PUT · DELETE |
/api/v1/admin/collections/:name/vector-config |
Read ({config, indexes}), set or remove the collection’s vector config. |
Two separate namespaces — see roles and permissions.
| Method |
Path |
Description |
| GET · POST |
/api/v1/admin/roles |
List custom roles plus built-ins, or create one (name, permissions) → 201 {role}. |
| GET · PUT · DELETE |
/api/v1/admin/roles/:name |
Fetch, replace permissions, or delete. Deleting is blocked while a membership still references the role. |
| GET · POST |
/api/v1/admin/enduser-roles |
Same, for end-user roles. |
| GET · PUT · DELETE |
/api/v1/admin/enduser-roles/:name |
Deleting is blocked while any user’s metadata.role still uses it. |
| Method |
Path |
Description |
| GET |
/api/v1/admin/users |
List end users (limit, skip) → {users}. |
| POST |
/api/v1/admin/users |
Create (email, password, optional name, emailVerified) → 201 {user}. |
| GET |
/api/v1/admin/users/:id |
Fetch one. |
| PATCH |
/api/v1/admin/users/:id |
Update email, name, emailVerified or metadata → 204. |
| POST |
/api/v1/admin/users/:id/activate · /deactivate · /block |
Change status → 204. Audited. |
| POST |
/api/v1/admin/users/:id/reset-mfa |
Clear the user’s TOTP → 204. Audited. |
| Method |
Path |
Description |
| GET |
/api/v1/admin/api-keys |
List keys — metadata only. |
| POST |
/api/v1/admin/api-keys |
Create (name, scopes) → 201. The raw key is returned here and never again. |
| DELETE |
/api/v1/admin/api-keys/:id |
Revoke → 204. |
| POST |
/api/v1/admin/api-keys/:id/rotate |
Rotate → the new raw key. |
Returns 404 storage is not enabled when no storage service is wired.
| Method |
Path |
Description |
| GET · POST |
/api/v1/admin/storage/buckets |
List buckets, or create one (name, access — default private) → 201 {bucket}. |
| PATCH |
/api/v1/admin/storage/buckets/:name |
Change access between public and private → 204. Audited. |
| DELETE |
/api/v1/admin/storage/buckets/:name |
Delete the bucket → 204. |
| Method |
Path |
Description |
| GET · POST |
/api/v1/admin/functions |
List, or create (name, trigger, optional timing, code, flow, enabled, tests, timeoutMs) → 201 {function}. |
| GET |
/api/v1/admin/functions/stats |
Aggregate run statistics. Matched before /:name. |
| GET · PATCH · DELETE |
/api/v1/admin/functions/:name |
Fetch, update or delete. |
| GET |
/api/v1/admin/functions/:name/logs |
Run logs (?limit=). |
| GET |
/api/v1/admin/functions/:name/versions |
Version history (?limit=). |
| GET |
/api/v1/admin/functions/:name/versions/:version |
One version. |
| POST |
/api/v1/admin/functions/:name/rollback |
Roll back to version → {function}. |
| POST |
/api/v1/admin/functions/:name/invoke |
Run manually and wait → {run}. |
| POST |
/api/v1/admin/functions/:name/tests/run |
Run the stored unit tests against a scratch database — never the project store, not metered, not logged as runs. |
| Method |
Path |
Description |
| GET · POST |
/api/v1/admin/integrations |
List (secrets redacted), or create (name, provider, optional config, secrets, enabled) → 201. |
| GET |
/api/v1/admin/integrations/providers |
The provider catalog. Matched before /:name. |
| GET · PATCH · DELETE |
/api/v1/admin/integrations/:name |
Fetch, update (config, secrets, enabled) or delete. |
| POST |
/api/v1/admin/integrations/:name/test |
Ad-hoc call: op, args, mode (live or dry), optional stub → {result, mode}. |
| PATCH · POST |
/api/v1/admin/integrations/:name/tests · /tests/run |
Save or run the stored test suite. |
| Method |
Path |
Description |
| GET · POST |
/api/v1/admin/webhooks |
List, or create (name, collection, events, url, optional headers, enabled) → 201. The signing secret is returned here and never again. |
| GET · PATCH · DELETE |
/api/v1/admin/webhooks/:name |
Fetch, update or delete. |
| GET |
/api/v1/admin/webhooks/:name/deliveries |
Delivery history (?limit=). |
| POST |
/api/v1/admin/webhooks/:name/rotate-secret |
Rotate the signing secret. |
| POST |
/api/v1/admin/webhooks/:name/test |
Fire a test delivery → {delivery}. |
| Method |
Path |
Description |
| GET |
/api/v1/admin/audit |
Audit log → {entries}. Filters: action, limit, skip. 404 when audit is off. |
| GET |
/api/v1/admin/metrics |
Per-plane request counters (auth, data, admin, graphql, storage, functions — ok vs error). |
| GET |
/api/v1/admin/usage |
Plan usage. When billing is disabled this returns 200 with {plan:null, limits:null, usage:null}, not a 404. |
| Method |
Path |
Description |
| GET |
/api/v1/admin/config-export |
The whole project shape as one versioned JSON document. OAuth client secrets are never included. |
| POST |
/api/v1/admin/config-import |
Merge-upsert by name — creates and updates, never deletes. ?dryRun=1 returns the plan without applying. Idempotent, though not transactional. |
| GET |
/api/v1/admin/introspect |
Candidate collections in a BYO-Mongo data database → {collections}. 404 unless the project is BYO. |
| GET |
/api/v1/admin/introspect/:collection |
Sampled report: stats, indexes, inferred schema, verdict. |
| POST |
/api/v1/admin/introspect/:collection/import |
Register a schema plus a deny-all policy. Writes nothing to the customer database. |
| POST |
/api/v1/admin/introspect/:collection/backfill |
Materialize id — the only write this feature makes to a customer database. |
| Method |
Path |
Description |
| GET |
/api/v1/admin/auth/oauth |
Provider config, always redacted, plus the deployment baseUrl to register as the callback. |
| PUT |
/api/v1/admin/auth/oauth/providers/:provider |
Upsert clientId, enabled and optionally clientSecret. Omitting the secret preserves the stored one. |
| PUT |
/api/v1/admin/auth/oauth/redirects |
Replace the allowed-redirect origins list → 204. |
/api/v1/control/*. If the deployment has no control plane, the whole subtree 404s.
| Method |
Path |
Description |
Auth |
| GET |
/api/v1/control/setup-status |
First-run probe so the dashboard can show the install wizard. Sits before the auth gate. |
public |
| POST |
/api/v1/control/auth/* |
The full end-user auth surface, for platform users. |
as per auth above |
| GET |
/api/v1/control/me |
The caller’s platform identity plus memberships. |
control token |
| GET · POST |
/api/v1/control/projects |
List visible projects with your role, or create one (name, optional mongoUrl for BYO Mongo) → 201 {project}. |
control token |
| GET |
/api/v1/control/projects/:id |
Project detail. |
member |
| PATCH |
/api/v1/control/projects/:id |
Update name, maxFileSize, color, icon → 204. |
Owner/Admin |
| DELETE |
/api/v1/control/projects/:id |
Delete → 204. |
Owner |
| POST |
/api/v1/control/projects/:id/suspend · /resume |
Suspend or resume → 204. |
control token |
| POST |
/api/v1/control/projects/:id/token |
Token exchange — mint a data-plane token for this project from your control token. This is the bridge between the two audiences. |
member |
| POST |
/api/v1/control/projects/:id/environments |
Create a linked environment cloned from the parent’s config. BYO Mongo is never inherited. |
Owner |
| Method |
Path |
Description |
Auth |
| GET |
/api/v1/control/projects/:id/members |
Members plus pending invites. |
control token |
| POST |
/api/v1/control/projects/:id/members |
Invite/add by email with role → 201 {member}. |
Owner/Admin |
| PATCH · DELETE |
/api/v1/control/projects/:id/members/:userId |
Change role, or remove → 204. |
Owner/Admin |
| DELETE |
/api/v1/control/projects/:id/members/pending/:email |
Cancel a pending invite (URL-encode the email) → 204. |
Owner/Admin |
Returns 404 connections are not enabled on this deployment when unwired.
| Method |
Path |
Description |
| GET |
/api/v1/control/connections/kinds |
The kind catalog. Registered before /:id, so kinds is never read as an id. |
| GET · POST |
/api/v1/control/connections |
List or create org-scoped connections (kind, name, optional config, secrets). |
| PATCH · DELETE |
/api/v1/control/connections/:id |
Update name/config/secrets, or delete. |
| GET · POST |
/api/v1/control/projects/:id/connections |
Merged project + org rows (project first; scope disambiguates), or create a project-scoped one. |
| Method |
Path |
Description |
Auth |
| POST |
/api/v1/control/environments/promote |
Promote config between two projects (fromProjectId, toProjectId). ?dryRun=1 returns the plan. Merge-upsert: a resource that exists only in the target is never deleted and never appears in the plan. |
Owner of both, same family |
| GET |
/api/v1/control/billing |
Plan, limits, subscription, owned projects with usage, the plan catalog. 404 when billing is disabled. |
control token |
| POST |
/api/v1/control/billing/checkout · /portal |
Stripe checkout or customer-portal session. 400 BILLING_DISABLED when off. |
control token |
| Method |
Path |
Description |
| GET |
/api/v1/control/admin/users |
All platform users (limit, skip). |
| POST |
/api/v1/control/admin/users/:id/plan |
Assign a plan slug. |
| PUT · DELETE |
/api/v1/control/admin/users/:id/limits |
Pin bespoke per-user limits, or clear the pin. |
| GET |
/api/v1/control/admin/plans |
The editable plan catalog. |
| PATCH |
/api/v1/control/admin/plans/:slug |
Patch name, priceUsdMonthly, limits, grandfather. |
| GET · POST |
/api/v1/control/admin/operators |
Active and pending super-admins, or invite by email. |
| DELETE |
/api/v1/control/admin/operators/:email |
Revoke a pending invite → 204. |
| GET · PUT |
/api/v1/control/admin/cost-model |
The unit-cost assumptions behind the price calculator. |
| Method |
Path |
Description |
Auth |
| POST |
/api/v1/billing/webhook |
Stripe event ingest. Returns 200 {received, applied} even for event types it ignores, so Stripe does not retry. |
the signature is the auth — a stripe-signature HMAC over the raw body |
Fails closed: billing disabled → 404; missing or invalid credentials, unset secret, non-JSON
body or a payload that is not Stripe-shaped → 400.
/api/v1/storage/*. There are two entry paths.
Anonymous reads are taken only for GET with a non-empty path and no Authorization
header. The project comes from ?project= (signed URLs embed it), then the header, then the
deployment default. This serves public buckets, and private objects when
?expires=<unix>&signature=<hmac> verifies.
Authenticated requests all need scope:storage:
| Method |
Path |
Description |
| GET |
/api/v1/storage/:bucket |
List files → {files}. Supports prefix, limit, skip. |
| POST |
/api/v1/storage/:bucket |
Create a folder (folder string) → 201 {folder}. |
| PUT |
/api/v1/storage/:bucket/*path |
Upload raw bytes. The content-type header is stored (default application/octet-stream) → 201 {file}. |
| GET |
/api/v1/storage/:bucket/*path |
Download bytes — or, with ?sign=<ttlSeconds>, return a signed URL instead of the file. |
| DELETE |
/api/v1/storage/:bucket/*path |
Delete → 204. |
Path segments after the bucket are percent-decoded and rejoined with /, so nested keys
work.
| Method |
Path |
Description |
Auth |
| GET/POST/PUT/PATCH/DELETE |
/api/v1/run/:project/*path |
Dispatch to an enabled http-trigger function whose declared {method, path} matches. |
optional — the handler self-gates |
The project (id or slug) is in the URL, so the endpoint is self-contained and needs no
header. Authentication is optional by design: a valid token yields a ctx.auth, while a
missing, expired, invalid or other-project token yields auth: null rather than an error —
the endpoint may legitimately be public, and the handler decides. See
functions.
Successful mutations overwhelmingly return 204 No Content. Creates return 201 with the
entity under a singular key — {document}, {role}, {user}, {function},
{integration}, {bucket}, {connection}, {project}, {member} — except
POST /admin/collections, which returns a bodyless 201. Listings use a plural key:
{documents}, {collections}, {users}, {entries}, {files}.
Error shapes are documented under errors.