API Overview
How ObjectStack generates its API surface from metadata — discovery, error handling wire formats, protocol types, and a map of the API & SDK docs.
API Overview
ObjectStack generates its entire API surface — REST endpoints, realtime protocols, and the client SDK — from your metadata: define an object once and its endpoints exist. This module documents those surfaces and how to consume them.
ObjectStack exposes a fully typed REST API. All endpoints use JSON request/response bodies. The API is service-driven — routes are only available when the corresponding plugin is installed. Use the Discovery endpoint to determine what services are available at runtime.
Surfaces at a glance
| Surface | Status in this repo |
|---|---|
| REST | ✅ Auto-generated from the protocol (@objectstack/rest) — CRUD, query, batch, metadata, packages |
| Realtime | ⚠️ In-process pub/sub service (@objectstack/service-realtime, single-instance); the /realtime/* REST routes and WebSocket/SSE transport are plugin-provided — none ships in the open framework |
| MCP | ✅ Non-system objects exposed automatically as Model Context Protocol tools; actions additionally require the author's ai.exposed opt-in — every call is gated by the caller's permissions/RLS (AI module) |
| GraphQL | ⚠️ Route is wired but bring-your-own service: /graphql returns 501 unless an implementation of the IGraphQLService contract is registered — none ships in the open framework |
| OData | ⚠️ Vocabulary only: REST list endpoints accept OData-style operators (e.g. $top), but there is no standalone OData endpoint |
Base URL: Configurable, defaults to /api/v1. All paths below are relative to the base URL.
Your app as an MCP server
REST and GraphQL are how code consumes your app. MCP is how AI consumes it.
Because every object and action is typed metadata, ObjectStack can expose the whole app
as a Model Context Protocol server — so an AI client
(Claude Code, Claude Desktop, Cursor, a local model) can inspect and operate the app
you built, under the same permissions and RLS as the UI. It is served at /api/v1/mcp
by default — set OS_MCP_SERVER_ENABLED=false to opt out
(see environment variables).
The generated tools mirror the surfaces you already defined:
list_objects / describe_object (discover the schema), query_records / get_record
(read), create_record / update_record / delete_record (write), and
list_actions / run_action (invoke your business actions by name). Every call runs as
the caller — RBAC, RLS, and field-level security all apply.
sequenceDiagram
participant AI as AI client (Claude / Cursor)
participant MCP as MCP server (@objectstack/mcp)
participant Eng as Action / ObjectQL engine
participant Sec as RBAC · RLS · FLS
participant DB as Data
AI->>MCP: run_action("resolve", { recordId })
MCP->>Eng: dispatch as the caller's principal
Eng->>Sec: check permissions on this record
Sec-->>Eng: allowed (or denied — fail-closed)
Eng->>DB: apply the change
DB-->>AI: result, same as the Console would return
See Actions as Tools for the run_action bridge and the
MCP reference for binding external MCP servers into your agents.
Authentication
Every REST call runs as a principal — anonymous requests only see what your permission model grants anonymous users. Two ways to authenticate:
- Session cookie (browsers, quick local tests):
POST /api/v1/auth/sign-in/emailwith{ "email": "…", "password": "…" }sets the session cookie — reuse it withcurl -c cookies.txt/-b cookies.txt. On a fresh dev database the seeded admin isadmin@objectos.ai/admin123. - API key (scripts, CI, headless agents): mint one with
POST /api/v1/keys(the key is shown once), or from Setup → Connect an Agent in the Console. Send it asx-api-key: osk_…orAuthorization: Bearer osk_….
See Authentication for the full identity surface (OAuth flows, sessions, providers) and Plugin Endpoints for the auth route catalog.
What's in this module
- Data API — CRUD, batch operations, record cloning, and analytics queries
- Metadata & Package API — object schemas, metadata types, UI views, and package management
- Plugin Endpoints — auth, workflow, realtime, and other plugin-provided routes
- Client SDK — the official TypeScript client
- Environment Routing — routing requests across environments
- Data Flow — how a request travels through the stack
- Error Handling (Client) — handling API errors in client code
- Error Handling (Server) — raising and shaping errors on the server
- Error Catalog — the full list of error codes
- Wire Format — request/response envelopes on the wire
Spec: HTTP API, Real-Time Protocols
Schema reference: API
Discovery
The discovery endpoint is the entry point for all clients. It returns the API version, available routes, service capabilities, and per-service status.
GET /api/v1
Returns the full discovery manifest.
Response:
{
"version": "1.0.0",
"apiName": "ObjectStack API",
"routes": {
"data": "/api/v1/data",
"metadata": "/api/v1/meta",
"analytics": "/api/v1/analytics"
},
"services": {
"metadata": {
"enabled": true,
"status": "available",
"route": "/api/v1/meta",
"provider": "objectql"
},
"data": { "enabled": true, "status": "available", "route": "/api/v1/data", "provider": "objectql" },
"analytics": { "enabled": true, "status": "available", "route": "/api/v1/analytics", "provider": "objectql" },
"auth": { "enabled": false, "status": "unavailable", "message": "Install plugin-auth to enable" }
},
"capabilities": {
"feed": { "enabled": false },
"automation": { "enabled": false },
"search": { "enabled": false }
}
}Disabled/uninstalled route keys (e.g. auth, workflow) are omitted from routes entirely rather than set to null; check services to tell "not installed" apart from "installed but not yet mounted here."
GET /.well-known/objectstack
Served by the runtime dispatcher (@objectstack/runtime), not @objectstack/rest — its body is wrapped as { "data": { ... } } and includes fields (name, environment, features, locale) that the @objectstack/rest-served /api/v1 response above does not. The client SDK's connect() tries /api/v1/discovery first and falls back to this endpoint, unwrapping either body.data or the bare body.
Service Status Values: available (fully operational), registered (route declared but handler unverified — may return 501), degraded (partial functionality), unavailable (not installed), stub (placeholder that throws errors)
Error Handling
Error responses depend on which HTTP server is in front of the kernel. There are two wire formats in use today.
Kernel REST server (@objectstack/rest) emits a string error message plus a SCREAMING_SNAKE code:
{
"error": "Record not found: account/123",
"code": "RECORD_NOT_FOUND"
}Validation failures additionally include a fields array (one entry per invalid field). Common codes emitted by the kernel REST server:
| Code | HTTP | Description |
|---|---|---|
VALIDATION_FAILED | 400 | Input validation failed (includes fields) |
PERMISSION_DENIED | 403 | Insufficient permissions |
RECORD_NOT_FOUND | 404 | Resource does not exist |
CONCURRENT_UPDATE | 409 | Record was modified by another user |
Runtime dispatcher (@objectstack/runtime) wraps errors in the { success: false, ... } envelope, where code is the numeric HTTP status:
{
"success": false,
"error": {
"message": "Record not found: account/123",
"code": 404,
"details": {}
}
}The richer ErrorResponseSchema in @objectstack/spec/api (with category, retryable, etc.) is the aspirational spec envelope, not the current wire format. Its category values are drawn from the ErrorCategory enum: validation, authentication, authorization, not_found, conflict, rate_limit, server, external, maintenance.
Protocol Types (Zod)
All request/response schemas are defined as Zod schemas in @objectstack/spec/api and can be used for both runtime validation and TypeScript type inference.
import {
FindDataRequestSchema,
FindDataResponseSchema,
type FindDataRequest,
type FindDataResponse,
} from '@objectstack/spec/api';
// Runtime validation
const request = FindDataRequestSchema.parse({ object: 'account', query: { ... } });
// TypeScript type
const response: FindDataResponse = await protocol.findData(request);See the Protocol Reference for the full list of protocol methods and their Zod schemas.