Skip to content

REST API

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 role201 {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.