ObjectOS
Reference

REST API

The HTTP surface ObjectOS exposes — generated from your metadata, scoped by permissions, OpenAPI-described.

REST API

Every object you declare gets a full REST endpoint set automatically. Every action becomes a POST. Every flow becomes a POST to /flows/.... There is no separate API layer to write or deploy.

Base URL: https://<your-host>/api/v1. OpenAPI spec is live at /api/v1/openapi.json.

Authentication

ObjectOS uses Better Auth. All routes under /api/v1 require an authenticated session unless explicitly marked public (see Public forms). Auth methods configured in apps/<app>/auth.config.ts — typically session cookie or bearer token from /api/v1/auth/sign-in.

Permissions are checked per route, per record. Routes annotated below with a permission key (e.g. ai:chat) call requirePermission(...); record-level access is enforced by RBAC + row-level security + field-level security inside the data engine.

Discovery

PathMethodPurpose
/api/v1GETService discovery — lists all registered routes, version, scoping mode
/api/v1/openapi.jsonGETOpenAPI 3.0 spec for every endpoint in the running runtime
/api/v1/searchGETFull-text search across searchable fields on all objects

Data — /api/v1/data/*

CRUD + advanced query for every object you declare. :object is the object's name (snake_case).

PathMethodPurpose
/data/:objectGETList / query — pass where, orderBy, limit, offset, cursor, expand, select as query params
/data/:object/queryPOSTAdvanced query — full ObjectQL body with groupBy + aggregations (see ObjectQL)
/data/:object/:idGETFetch one record. Supports ?select= and ?expand=
/data/:objectPOSTCreate. Returns 201 with the new record
/data/:object/:idPATCHUpdate. Pass If-Match: <version> header (or expectedVersion in body) for optimistic concurrency — 409 CONCURRENT_UPDATE on conflict
/data/:object/:idDELETEDelete. Same If-Match rule
/data/:object/importPOSTBulk import (CSV or JSON). Body: { format, csv? | rows?, mapping?, dryRun? }. Max 5,000 rows/request
/data/:object/exportPOSTExport records as csv / xlsx / pdf / json
/data/:object/:id/sharesGET / POSTPer-record sharing — list grants, grant access
/data/:object/:id/shares/:shareIdDELETERevoke a share
/data/lead/:id/convertPOSTSalesforce-style lead conversion (CRM template). Body: { accountId?, contactId?, createOpportunity? }

Quick example

GET /api/v1/data/support_ticket?where={"status":{"$eq":"open"}}&orderBy=-created_at&limit=20
Authorization: Bearer <token>
{
  "items": [ { "id": "...", "subject": "...", "status": "open" } ],
  "total": 137,
  "nextCursor": "eyJpZCI6IjAxSDA..."
}

AI — /api/v1/ai/*

The endpoints the AI Builder and your agents ride on.

PathMethodPermissionPurpose
/ai/modelsGETai:chatList available models from the configured providers
/ai/chatPOSTai:chatSingle-shot chat completion (sync)
/ai/chat/streamPOSTai:chatStreaming chat (SSE)
/ai/completePOSTai:chatRaw completion endpoint
/ai/conversationsGET / POSTai:chatList / create persistent conversations
/ai/conversations/:idGET / DELETEai:chatRead / delete a conversation
/ai/conversations/:id/messagesPOSTai:chatAppend a user message and run the agent
/ai/pending-actionsGETai:readThe HITL approval queue — mutations the AI proposed
/ai/pending-actions/:idGETai:readInspect a queued mutation
/ai/pending-actions/:id/approvePOSTai:approveApply the mutation
/ai/pending-actions/:id/rejectPOSTai:approveDiscard it

The split between ai:read and ai:approve is the security primitive that makes the AI Builder safe for end users.

Actions — /api/v1/actions/*

Every *.action.ts you declare becomes an endpoint.

PathMethodPurpose
/actions/:actionPOSTInvoke the action. Body is the action's input schema. Permissions and input validation come from the action declaration

Each action is also exposed to the AI as a action_<name> tool — see Actions.

Flows — /api/v1/flows/*

PathMethodPurpose
/flows/:flow/startPOSTStart a flow. Body is the flow input
/flows/:flow/runs/:runIdGETStatus + step output of a running / completed flow
/flows/:flow/runs/:runId/cancelPOSTCancel an in-flight run

Metadata — /api/v1/meta/*

Read-only introspection into the running runtime's metadata. Useful for tooling and the Console.

PathMethodPurpose
/metaGETAll metadata types (object, view, action, flow, agent, …)
/meta/:typeGETList items of a type. Filter with ?package=<id>
/meta/:type/:nameGETFetch one item. ?layers=true returns the 3-state diff view (base / package / overlay)
/meta/:type/:name/referencesGETFind every metadata item that references this one
/meta/:type/:name/historyGETAudit trail for that item
/meta/:type/:section/:nameGET / POSTRead / upsert a sectioned metadata item
/meta/:type/:section/:nameDELETEDelete a sectioned item

Sharing & approvals

PathMethodPurpose
/sharing/rulesGET / POST / DELETEManage sharing rules
/sharing/rules/:idOrName/evaluatePOSTDry-run a rule against a context
/approvals/processesGET / POSTApproval process definitions
/approvals/requestsGET / POSTSubmit / list approval requests
/approvals/requests/:id/approvePOSTDecision: approve
/approvals/requests/:id/rejectPOSTDecision: reject
PathMethodPurpose
/reportsGET / POST / PATCHReport CRUD
/reports/:id/runPOSTExecute a report — returns aggregated rows
/reports/:id/schedulePOSTSchedule recurring delivery

Public forms

The only routes that are unauthenticated, opt-in via FormView.sharing.allowAnonymous.

PathMethodAuth
/forms/:slugGETpublic — returns the form schema
/forms/:slug/submitPOSTpublic — submits a response

Utilities

PathMethodPermissionPurpose
/email/sendPOSTemail:sendSend an email through the configured provider

Error envelope

Every non-2xx response follows the same shape:

{
  "error": {
    "code": "PERMISSION_DENIED",
    "message": "Missing ai:approve on conversation 01H…",
    "details": { "permission": "ai:approve", "subject": "01H…" }
  }
}

Common codes: UNAUTHENTICATED, PERMISSION_DENIED, VALIDATION_ERROR, NOT_FOUND, CONCURRENT_UPDATE, RATE_LIMITED, INTERNAL.

Versioning

/api/v1 is the stable API. Breaking changes go to /api/v2 and both serve in parallel for one minor release. The current version and deprecation horizon are in GET /api/v1.

See also

On this page