The Plugin API Surface by Example
Your plugin exposes its own API surface — the fourth of the platform's four API surfaces — for the domain operations the core doesn't provide (entity CRUD, workspace management, submitting input, and so on). Here's how it's mounted, how it authenticates, and the conventions it follows.
Where it mounts, and its auth
Your router mounts at /api{apiRoutePrefix} — a manifest apiRoutePrefix of /default-plugin serves at /api/default-plugin. Authentication is operator JWT, applied by the plugin loader when it mounts your routes — so your individual handlers don't apply auth middleware themselves. Your endpoints are also subject to the core's rate limiting.
The response envelope
Follow the platform's standard envelope, the same one the core uses:
- success (single):
{ "data": { ... } } - success (list):
{ "data": [ ... ], "meta": { ... } } - error:
{ "error": { "status", "code", "message", "details?" } }
Prefix your own error codes with your plugin slug (e.g. DEFAULT_PLUGIN_SECTION_NOT_EMPTY) so they're unambiguous.
What your endpoints typically expose
The default plugin's router is a good template:
- Entity CRUD —
GET/POST/PATCH/DELETE /api/{prefix}/things; - Workspace management — switch active workspace, finalize / unfinalize;
- Plugin-specific operations — submit input, get a cost summary, manage sections.
What they should NOT expose
Two things to avoid, because they break the boundary or duplicate the core:
- Don't duplicate core endpoints. Workflow start/log/complete, the configuration registries, retry — those are the core's; your plugin calls them, it doesn't re-implement them.
- Don't cross plugins. Your API is for your domain; plugins don't know about each other, so no entity-to-entity linkage across plugins.
And the boundary rules apply here too: your handlers mutate core tables only through core services, never directly.
The authoritative contract
This is the shape and the conventions; each plugin also has its own detailed API contract document (the default plugin's is 009b). Build your endpoints to the envelope, mount them under your one prefix, lean on operator-JWT auth from the loader, and keep to your own domain — and your API slots cleanly into the platform's four-surface model.