---
title: API Reference
description: Complete REST API reference for ObjectStack — all endpoints, request/response schemas, and service availability.
---

# API Reference

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](#discovery) to determine what services are available at runtime.

<Callout type="info">
**Base URL**: Configurable, defaults to `/api/v1`. All paths below are relative to the base URL.
</Callout>

---

## 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**:
```json
{
  "name": "ObjectOS",
  "version": "1.0.0",
  "environment": "development",
  "routes": {
    "data": "/api/v1/data",
    "metadata": "/api/v1/meta",
    "analytics": "/api/v1/analytics",
    "auth": null,
    "workflow": null
  },
  "features": {
    "graphql": false,
    "search": false,
    "websockets": false,
    "files": false,
    "analytics": true,
    "ai": false,
    "workflow": false,
    "notifications": false,
    "i18n": false
  },
  "services": {
    "metadata": {
      "enabled": true,
      "status": "degraded",
      "route": "/api/v1/meta",
      "provider": "kernel",
      "message": "In-memory registry; DB persistence pending"
    },
    "data": { "enabled": true, "status": "available", "route": "/api/v1/data", "provider": "kernel" },
    "analytics": { "enabled": true, "status": "available", "route": "/api/v1/analytics" },
    "auth": { "enabled": false, "status": "unavailable", "message": "Install an auth plugin to enable" }
  },
  "locale": {
    "default": "en",
    "supported": ["en", "zh-CN"],
    "timezone": "UTC"
  }
}
```

### `GET /.well-known/objectstack`

Alias for the discovery endpoint used by auto-discovery in the client SDK.

<Callout type="info">
**Service Status Values**: `available` (fully operational), `degraded` (partial functionality), `unavailable` (not installed), `stub` (placeholder that throws errors)
</Callout>

---

## Metadata

Retrieve and manage object schemas, metadata types, and UI views. Always available — provided by the kernel.

### `GET /meta`

List all registered metadata types.

**Response**: `{ types: ["object", "view", "plugin", ...] }`

### `GET /meta/:type`

List all items of a metadata type.

| Parameter | Location | Description |
|:----------|:---------|:------------|
| `type` | path | Metadata type name (e.g. `object`, `view`) |

**Response**: `{ type: "object", items: [{ name: "account", ... }, ...] }`

### `GET /meta/:type/:name`

Get a specific metadata item with optional ETag caching.

| Parameter | Location | Description |
|:----------|:---------|:------------|
| `type` | path | Metadata type name |
| `name` | path | Item name (snake_case) |
| `If-None-Match` | header | ETag for conditional request |

**Response**: `{ type: "object", name: "account", item: { ... } }`  
**304** if ETag matches (not modified).

### `PUT /meta/:type/:name`

Create or update a metadata item.

**Body**: `{ type: "object", name: "account", item: { ... } }`  
**Response**: `{ success: true }`

### `GET /ui/view/:object/:type`

Get an auto-generated UI view for an object.

| Parameter | Location | Description |
|:----------|:---------|:------------|
| `object` | path | Object name (snake_case) |
| `type` | path | `list` or `form` |

**Response**: Full `ViewSchema` definition with columns/fields derived from the object schema.

---

## Data Operations

CRUD operations on any object. Always available — provided by the kernel.

### `GET /data/:object`

Query records with filtering, sorting, selection, and pagination.

| Parameter | Location | Description |
|:----------|:---------|:------------|
| `object` | path | Object name |
| `select` | query | Comma-separated field names |
| `filter` | query | Filter expression (JSON). `filters` also accepted for backward compatibility. |
| `sort` | query | Sort expression (e.g. `name asc` or `-created_at`) |
| `top` | query | Limit (default: 20) |
| `skip` | query | Offset |
| `expand` | query | Comma-separated list of relations to eager-load |
| `search` | query | Full-text search query |

> **Note:** OData-style `$`-prefixed parameters (`$filter`, `$select`, `$orderby`, `$top`, `$skip`) are supported via the [OData endpoint](/docs/references/api/odata). The standard REST API uses unprefixed parameter names.

**Response**:
```json
{
  "object": "account",
  "records": [{ "id": "1", "name": "Acme Corp", ... }],
  "total": 42,
  "hasMore": true
}
```

### `GET /data/:object/:id`

Get a single record by ID. Only `select` and `expand` query parameters are allowed; all other parameters are discarded.

| Parameter | Location | Description |
|:----------|:---------|:------------|
| `object` | path | Object name |
| `id` | path | Record ID |
| `select` | query | Comma-separated field names to include |
| `expand` | query | Comma-separated list of relations to eager-load |

**Response**: `{ object: "account", id: "1", record: { ... } }`

### `POST /data/:object`

Create a new record.

**Body**: `{ name: "Acme Corp", industry: "Technology" }`  
**Response**: `{ object: "account", id: "1", record: { ... } }`

### `PATCH /data/:object/:id`

Update an existing record (partial update).

**Body**: `{ industry: "Healthcare" }`  
**Response**: `{ object: "account", id: "1", record: { ... } }`

### `DELETE /data/:object/:id`

Delete a record.

**Response**: `{ object: "account", id: "1", success: true }`

---

## Batch Operations

Efficient bulk operations. Always available.

### `POST /data/:object/batch`

Execute a batch operation (create / update / upsert / delete) on multiple records.

**Body**:
```json
{
  "operation": "update",
  "records": [
    { "id": "1", "data": { "status": "active" } },
    { "id": "2", "data": { "status": "active" } }
  ],
  "options": {
    "atomic": true,
    "returnRecords": true,
    "continueOnError": false,
    "validateOnly": false
  }
}
```

**Response**: `BatchUpdateResponse` with `succeeded`, `failed`, `errors`, and optionally `records`.

### `POST /data/:object/createMany`

Batch create multiple records.

**Body**: `{ records: [{ name: "A" }, { name: "B" }] }`  
**Response**: `{ object: "account", records: [...], count: 2 }`

### `POST /data/:object/updateMany`

Batch update multiple records.

**Body**:
```json
{
  "records": [
    { "id": "1", "data": { "status": "active" } },
    { "id": "2", "data": { "status": "closed" } }
  ],
  "options": { "atomic": false }
}
```

### `POST /data/:object/deleteMany`

Batch delete records by ID list.

**Body**: `{ ids: ["1", "2", "3"] }`

---

## Analytics

Semantic BI queries using a cube-style API. Available when the analytics service is enabled.

### `POST /analytics/query`

Execute an analytics query.

**Body**:
```json
{
  "cube": "account",
  "measures": ["revenue.sum", "count"],
  "dimensions": ["industry"],
  "filters": [{ "member": "status", "operator": "equals", "values": ["active"] }],
  "limit": 100
}
```

**Response**:
```json
{
  "data": [
    { "industry": "Technology", "revenue.sum": 150000, "count": 5 },
    { "industry": "Healthcare", "revenue.sum": 80000, "count": 3 }
  ],
  "annotation": {
    "measures": { "revenue.sum": { "title": "Revenue Sum", "type": "number" } },
    "dimensions": { "industry": { "title": "Industry", "type": "string" } }
  }
}
```

### `GET /analytics/meta`

Get auto-generated cube metadata for all objects.

**Response**: Array of cube definitions with measures, dimensions, and time dimensions.

### `POST /analytics/sql`

Execute a raw SQL analytics query (if supported by driver).

---

## Package Management

Manage installed plugins/packages.

### `GET /packages`

List installed packages.

### `POST /packages`

Install a package from manifest.

**Body**: `{ manifest: { name: "plugin-auth", version: "1.0.0", ... } }`

### `GET /packages/:id`

Get package details.

### `DELETE /packages/:id`

Uninstall a package.

### `PATCH /packages/:id/enable`

Enable a disabled package.

### `PATCH /packages/:id/disable`

Disable a package without uninstalling.

---

## Authentication (Plugin Required)

<Callout type="warn">
These endpoints are only available when an auth plugin is installed. Check `discovery.services.auth.enabled` first.
</Callout>

### `POST /auth/login`

Authenticate and receive a session token.

**Body**: `{ username: "admin", password: "secret" }`  
**Response**: `{ token: "jwt...", expiresAt: "...", user: { ... } }`

---

## Plugin-Provided Service Endpoints

The following endpoints become available when the corresponding plugin is installed and registered with the kernel. Use the discovery `services` map to check availability.

### Workflow (`/workflow`) — Plugin Required

| Method | Endpoint | Description |
|:-------|:---------|:------------|
| GET | `/workflow/:object/config` | Get workflow configuration |
| GET | `/workflow/:object/:recordId/state` | Get record's workflow state |
| POST | `/workflow/:object/:recordId/transition` | Execute state transition |
| POST | `/workflow/:object/:recordId/approve` | Approve workflow step |
| POST | `/workflow/:object/:recordId/reject` | Reject workflow step |

### Automation (`/automation`) — Plugin Required

| Method | Endpoint | Description |
|:-------|:---------|:------------|
| POST | `/automation/trigger` | Trigger an automation flow |

### Views (`/ui`) — Plugin Required

| Method | Endpoint | Description |
|:-------|:---------|:------------|
| GET | `/ui/views?object=:object` | List views for an object |
| GET | `/ui/views/:viewId` | Get a view definition |
| POST | `/ui/views` | Create a new view |
| PATCH | `/ui/views/:viewId` | Update a view |
| DELETE | `/ui/views/:viewId` | Delete a view |

### Realtime (`/realtime`) — Plugin Required

| Method | Endpoint | Description |
|:-------|:---------|:------------|
| POST | `/realtime/connect` | Establish WebSocket/SSE connection |
| POST | `/realtime/disconnect` | Close connection |
| POST | `/realtime/subscribe` | Subscribe to channel |
| POST | `/realtime/unsubscribe` | Unsubscribe |
| POST | `/realtime/presence` | Set presence |
| GET | `/realtime/presence/:channel` | Get channel presence |

### Notifications (`/notifications`) — Plugin Required

| Method | Endpoint | Description |
|:-------|:---------|:------------|
| GET | `/notifications` | List notifications |
| POST | `/notifications/read` | Mark as read |
| POST | `/notifications/read-all` | Mark all as read |
| POST | `/notifications/devices` | Register device |
| DELETE | `/notifications/devices/:id` | Unregister device |
| GET | `/notifications/preferences` | Get preferences |
| PATCH | `/notifications/preferences` | Update preferences |

### AI (`/ai`) — Plugin Required

| Method | Endpoint | Description |
|:-------|:---------|:------------|
| POST | `/ai/nlq` | Natural language → query |
| POST | `/ai/chat` | AI chat conversation |
| POST | `/ai/suggest` | Get value suggestions |
| POST | `/ai/insights` | Get data insights |

### i18n (`/i18n`) — Plugin Required

| Method | Endpoint | Description |
|:-------|:---------|:------------|
| GET | `/i18n/locales` | List available locales |
| GET | `/i18n/translations?locale=:locale` | Get translation bundle |
| GET | `/i18n/labels/:object?locale=:locale` | Get field labels |

### GraphQL (`/graphql`) — Plugin Required

| Method | Endpoint | Description |
|:-------|:---------|:------------|
| POST | `/graphql` | Execute GraphQL query/mutation |

### File Storage (`/storage`) — Plugin Required

| Method | Endpoint | Description |
|:-------|:---------|:------------|
| POST | `/storage/upload` | Upload a file |
| GET | `/storage/:id` | Download a file |
| DELETE | `/storage/:id` | Delete a file |

---

## Error Handling

All error responses follow a standardized format:

```json
{
  "error": {
    "code": "resource_not_found",
    "message": "Record not found: account/123",
    "httpStatus": 404,
    "category": "request",
    "retryable": false,
    "details": {}
  }
}
```

### Error Codes

| Code | HTTP | Category | Retryable | Description |
|:-----|:-----|:---------|:----------|:------------|
| `validation_error` | 400 | validation | No | Input validation failed |
| `invalid_query` | 400 | validation | No | Malformed query |
| `unauthenticated` | 401 | auth | No | Authentication required |
| `permission_denied` | 403 | auth | No | Insufficient permissions |
| `resource_not_found` | 404 | request | No | Resource does not exist |
| `conflict` | 409 | request | No | Resource conflict (e.g. duplicate) |
| `rate_limit_exceeded` | 429 | rate_limit | Yes | Too many requests |
| `internal_error` | 500 | server | Yes | Server error |
| `service_unavailable` | 503 | server | Yes | Service temporarily unavailable |

---

## 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.

```typescript
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](../references/api/protocol) for the complete list of 57 protocol methods and their Zod schemas.

---

## Next Steps

<Cards>
  <Card 
    href="./client-sdk" 
    title="Client SDK"
    description="TypeScript client that wraps this API"
  />
  <Card 
    href="./kernel-services" 
    title="Kernel Services"
    description="Service implementation checklist for plugin authors"
  />
  <Card 
    href="../concepts/implementation-status" 
    title="Implementation Status"
    description="What is implemented today"
  />
</Cards>
