| title | Plugin endpoints — add routes from a plugin |
|---|---|
| navTitle | Plugin Endpoints |
| description | REST endpoints each installed plugin adds — auth, workflow, automation, views, realtime, notifications, AI, i18n and files — and how to discover them. |
These REST endpoints are only available when the corresponding plugin is installed. Check the discovery manifest services map before calling them; all paths are relative to the base URL (defaults to /api/v1).
This page is a catalog of routes the platform serves for you — it is not about authoring an endpoint of your own. To publish your own URL as metadata, see Declarative Endpoints.
These endpoints are only available when an auth plugin is installed. Check `discovery.services.auth.enabled` first.Authenticate with email and password (better-auth's email sign-in route, mounted under the auth plugin's /auth prefix) and receive a session token. There is no /auth/login route.
Body: { email: "admin@example.com", password: "secret" }
Response: { token: "jwt...", user: { ... } }
The following endpoints become available when the corresponding plugin is installed and registered with the kernel. Use the discovery services map to check availability.
Approve/reject were never workflow routes (ADR-0019): approval is a flow node, and decisions are recorded on the approvals runtime via POST /approvals/requests/:id/approve and POST /approvals/requests/:id/reject.
| Method | Endpoint | Description |
|---|---|---|
| POST | /automation/:name/trigger |
Trigger an automation flow by name (legacy alias: /automation/trigger/:name). Failures answer real status codes, not a 200 wrapping an inner failure: 404 unknown flow, 409 FLOW_DISABLED, 422 FLOW_NO_START_NODE, 422 FLOW_INPUT_SCHEMA_INVALID, 400 FLOW_FAILED for a run that ran and was rejected — see Run a flow via API |
The automation dispatcher also exposes flow CRUD (POST /automation, GET/PUT/DELETE /automation/:name) and run observability/resume routes — see Durable pause & resume.
To list flows, read the metadata plane: GET /meta/flow (client.meta.getItems('flow')) — flows are metadata (ADR-0106). There is no GET /automation list route; it was retired, and a GET to that path gets the host's method-mismatch answer (405 with Allow: POST on the default server), because only POST lives there. Per-flow runtime enablement and trigger binding is GET /automation/_status (client.automation.getRuntimeStatus()).
| Method | Endpoint | Description |
|---|---|---|
| GET | /ui/views/:object |
List views for an object |
| GET | /ui/views/:object/:viewId |
Get a view definition |
| POST | /ui/views/:object |
Create a new view |
| PATCH | /ui/views/:object/:viewId |
Update a view |
| DELETE | /ui/views/:object/:viewId |
Delete a view |
| 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 |
| PUT | /realtime/presence/:channel |
Set presence |
| GET | /realtime/presence/:channel |
Get channel presence |
| Method | Endpoint | Description |
|---|---|---|
| GET | /notifications |
List notifications (inbox) |
| POST | /notifications/read |
Mark notifications as read (body: { ids: string[] }) |
| POST | /notifications/read/all |
Mark all as read |
These are the routes service-ai mounts, and the SDK method that reaches each:
| Method | Endpoint | SDK | Description |
|---|---|---|---|
| POST | /ai/chat |
ai.chat / ai.chatStream |
Chat completion — JSON with stream: false, otherwise the Vercel UI Message Stream |
| POST | /ai/chat/stream |
— | Generic-SSE twin of /ai/chat: the same completion without the tool loop or persistence |
| POST | /ai/complete |
ai.complete |
Text completion |
| GET | /ai/models |
ai.models |
Models this system offers (cloud ADR-0028) |
| GET | /ai/status |
— | Active adapter provenance (console diagnostics) |
| GET | /ai/effective-model |
— | Resolved model ids and their source (console diagnostics) |
| POST / GET | /ai/conversations |
ai.conversations.create / .list |
Create / list conversations — the list is newest first and pages by keyset: send the id of the last conversation you hold as cursor, and read hasMore to learn whether another page follows |
| GET / PATCH / DELETE | /ai/conversations/:id |
ai.conversations.get / .update / .delete |
Read / update / delete |
| POST | /ai/conversations/:id/messages |
ai.conversations.addMessage |
Append a message |
The three phantom routes were declared in DEFAULT_AI_ROUTES and called by
client.ai.nlq/suggest/insights; every call 404ed. v17 removed that SDK
namespace rather than build endpoints for it, and the same issue then gave
the SDK the surface that does exist — the ai.* column above.
Reviewed dispositions for all 12 routes live in the cloud repo,
packages/service-ai/src/ai-route-ledger.ts, whose conformance test reads
buildAIRoutes() and drives the SDK against it. The three rows with no SDK
method are deliberate: /status and /effective-model are operator
diagnostics, and /chat/stream is superseded by /chat's streaming mode.
For a React chat UI prefer useChat() (@ai-sdk/react) — it speaks the same
protocol ai.chatStream parses and owns message state for you.
| Method | Endpoint | Description |
|---|---|---|
| GET | /i18n/locales |
List available locales |
| GET | /i18n/translations/:locale |
Get translation bundle |
| GET | /i18n/labels/:object/:locale |
Get field labels |
Provided by @objectstack/service-storage, which registers these routes on the
host HTTP server. Uploads are a three-step protocol — ask for a presigned
target, send the bytes straight to it, then commit — so the bytes never proxy
through the API server, and the same shape works for S3, local disk, or any
other adapter.
| Method | Endpoint | SDK | Description |
|---|---|---|---|
| POST | /storage/upload/presigned |
storage.getPresignedUrl |
Step 1 — mint an upload target for a new sys_file row |
| POST | /storage/upload/complete |
storage.upload |
Step 3 — commit the uploaded bytes; with attachTo, also attach the file to a record as the caller |
| POST | /storage/upload/chunked |
storage.initChunkedUpload |
Open a chunked / resumable session |
| PUT | /storage/upload/chunked/:uploadId/chunk/:chunkIndex |
storage.uploadPart |
Send one chunk |
| POST | /storage/upload/chunked/:uploadId/complete |
storage.completeChunkedUpload |
Assemble the parts |
| GET | /storage/upload/chunked/:uploadId/progress |
storage.resumeUpload |
Read session progress (first step of a resume) |
| GET | /storage/files/:fileId/url |
storage.getDownloadUrl |
Resolve a short-lived signed download URL |
| GET | /storage/files/:fileId |
— | Stable browser URL; 302s to the same signed URL (what file fields carry) |
storage.upload(file) runs steps 1–3 for you, including the direct-to-storage
PUT in step 2. Step 1 refuses an empty mimeType with 400 INVALID_REQUEST,
so for a file whose type the browser does not know (file.type === '') the
helper sends application/octet-stream; a typed file keeps its own type.
To attach the upload to a record's Attachments, send attachTo: { parentObject, parentId } with step 3 (and start the upload with scope: 'attachments'): the
commit and the sys_attachment row are one unit, written as the signed-in
caller, and the answer carries the attachmentId
(attachment access). A refused attach
leaves the upload pending.
A file field takes the fileId only after step 3. A signed-in record write
that names a file whose upload was never completed is refused with 400 VALIDATION_FAILED, one invalid_value entry naming the field and the file,
and the file is not claimed. Complete the upload, then save the record again.
Both routes are retired. /storage is service-storage's surface — install that
package to get it. Discovery advertises the route only when the occupant of the
storage slot (spelled file-storage before #9683; that spelling stays a
deprecated v17 alias) actually mounts HTTP handlers, so an in-memory dev
implementation now reports handlerReady: false and no routes.storage
instead of pointing at a path with nothing behind it.
- API Overview — discovery, error handling, and protocol types
- Client SDK — service-aware TypeScript client that adapts to installed plugins
- Declarative Endpoints — declaring an endpoint of your own (
apis:), the other half of this page's name