REST API reference
The Workspace exposes its capabilities over a REST API rooted at your workspace URL. Most enterprise integrations will use only a small slice of this surface — the custom endpoints you write and a handful of admin/auth endpoints.
This page describes the contract for the built-in endpoints. For your own endpoints, see Custom Endpoints.
Base URL
https://<your-workspace-host>/api/
For local development that is typically http://localhost:8080/api/.
Authentication
All authenticated endpoints accept a bearer token:
Authorization: Bearer <token>
See Token scopes for the three token types (session JWT, API token, endpoint token) and how to choose between them.
Unauthenticated endpoints — at the moment, only the login routes — accept and return JSON without an Authorization header.
Content type
Request and response bodies are JSON unless explicitly noted. Use:
Content-Type: application/json
Accept: application/json
Standard response envelopes
Successful responses are bare JSON values shaped per endpoint. Errors share the envelope documented in Error codes.
Pagination
List endpoints accept skip and take query parameters and return:
{
"items": [ /* ... */ ],
"total": 1248,
"skip": 0,
"take": 50
}
Always treat total as advisory — for very large traversals it may be a lower bound. Iterate by checking items.length === take until it isn't.
Authentication endpoints
Exchange a username/password for a session JWT.
- Body
{ "user": "...", "password": "..." }- Returns
{ "token": "...", "expiresAt": "..." }
Verify a session JWT is still valid.
- Auth
- Bearer (session JWT)
- Returns
{ "user": { "uid": "...", "name": "..." } }or401.
Change the calling user's password.
- Auth
- Bearer (session JWT)
- Body
{ "oldPassword": "...", "newPassword": "..." }
Start a password-reset flow for an email address.
- Body
{ "email": "..." }
SSO endpoints
Each SSO provider exposes the same shape under a provider-specific prefix. The provider names are microsoftsso, googlesso, oktasso, auth0sso.
Returns the URL clients should redirect to when starting an SSO login.
Callback the identity provider redirects back to after the user authenticates. Configure this exact path as the redirect URI in your IdP.
SAML follows a different pattern:
Builds an AuthnRequest for the configured provider.
See the individual SSO guides (Microsoft, Google, Okta, Auth0) for end-to-end setup.
Custom endpoint invocation
Invoke an endpoint by name as the authenticated user. Used by the workspace's own front-end and by any caller using a user session JWT.
- Auth
- Bearer (session JWT, or API token with the
endpoints:runscope) - Body
- the request shape your endpoint expects (
ParseBody<T>()). - Returns
- whatever the endpoint returns (typed JSON).
Invoke an endpoint with an endpoint token. The endpoint's CurrentUser is the token's associated user (or null if the token is system-scoped).
- Auth
- Bearer (endpoint token)
- Body / Returns
- same as
/run/{name}.
Lower-level invocation alias used by the front-end. Same auth and behavior as /api/endpoints/run/{name}.
Example — calling a custom endpoint
curl -X POST "https://workspace.example.com/api/endpoints/run/similar-tickets" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "screen flicker after firmware update",
"productSku": "PRO-14",
"limit": 5
}'
[
{
"UID": "abc123…",
"Type": "Ticket",
"Id": "T-9182",
"Subject": "Screen flicker after 5.1.2 firmware",
"Body": "…"
}
]
AI chat and tool metrics
Per-tool invocation metrics (counts, average latency, error rate). Admin only.
Search configuration
Every term that has synonyms configured for a language. Pages with the X-MSK-SKIP / X-MSK-LIMIT headers and answers { Results, TotalCount }; with includeSynonyms=1 each row is a [term, linkedTo] pair. Requires the Can Manage Synonyms role.
The synonyms, alternates, blocked terms and suggested candidates for one term. tolerance (0.7–1.0) sets how similar a suggestion has to be. Requires the Can Manage Synonyms role.
Store the Synonyms, Alternates and Blocked lists for one term — all three are required, send [] for unused ones. Writes one direction only; mirror the call to link a pair. replace=false merges with what is configured. Requires the Can Manage Synonyms role, write access.
Language codes are the usual two-letter codes, with -- for "any language". See
Synonyms for what each list does.
Assistant configuration
Which node types and search filters the built-in search tool exposes to an assistant, plus every filter available for the enabled node types. Admin only.
Store the exposed node types and filters. An empty set restores the built-in defaults. Admin, write access.
The deep research queue limits, the workspace's researchers, and the runs executing or queued right now. Admin only.
Store the queue limits and per-run ceilings. A value of zero clears the override and restores the default. Admin, write access.
Stop the deep research running in a conversation. Admin, write access.
See The search tool and Deep research for what these configure.
These are the operational metrics shown in Settings → Monitoring. There is currently no public Prometheus /metrics endpoint; see Monitoring for the roadmap and external-monitoring options.
Administration and diagnostics
The endpoints listed below require a system-administrator session; reads accept a read-only administrator, writes do not. The behaviour changes at the end of the section also cover endpoints an ordinary signed-in user can reach, and say so per entry.
Everything the admin Build tree draws, in one call and with no source code in it. Compile-free — AI-tool health comes from each node's stored precompiled flag. Admin, read-only.
Which catalog items do not compile, read from the compile caches rather than by compiling on the request. Admin, read-only.
- Returns
{ "FailingIds": [ "…" ], "Pending": true|false }.Pending: truemeans a cache has not been warmed yet and a background warm-up has just started — poll again rather than treating an emptyFailingIdsas "everything compiles".
The workspace change-history timeline, newest first, backing Manage → Operate → Change History. Admin, read-only.
The per-node-type / per-edge-type database counters, as the aligned table the server logs periodically. Admin, read-only. text/plain.
The object-pool counters, as the table the server logs next to the database statistics. Pools are process-wide, so this is not per-graph. Admin, read-only. text/plain.
Every match of a historical log search, streamed as one .log file. Admin, read-only.
Attach a file to an admin-assistant conversation. Admin, write access.
- Body
multipart/form-datawith one file.- Returns
- the same file-entry result the chat composer uses —
{ "UID": "…", "Size": …, "ContentType": "…" }. Uploading the same name twice answers409carrying the entry already there, rather than making a second copy. The file is stored as an ordinary file entry in the administrator's upload folder, with an edge from the conversation, and the assistant reads it at/home/uploads/<name>. It is deleted with the conversation.
Streamed responses (server-sent events)
Two endpoints that apply definitions to a workspace answer with a text/event-stream instead of a single JSON body, because compiling and rebuilding what a change touches outlasts what a browser — or a proxy in front of it — holds a plain request open for:
POST /api/admin-agent/sessions/{id}/commit/approvePOST /api/graph/import-definitions
The event sequence is the same for both: one event: log per step, then one event: result carrying exactly the payload the endpoint used to answer with, then event: done. A keepalive SSE comment (:) is written when nothing has happened for ten seconds. A caller that only wants the outcome can read the last result event and ignore the rest.
Two things to keep in mind when calling either directly:
- A refusal before the stream opens is still an ordinary status code.
import-definitionschecks the upload's content type first and answers a plain400for anything that is not a zip; the approval endpoint answers404for a session belonging to someone else. - Closing the connection does not stop the work. Neither apply is tied to the request's cancellation — the approval runs on the server's shutdown token and the import takes no token at all — so a client that goes away loses the log, not the change.
Behaviour changes
- Access groups (signed-in user).
PUT /api/groups/group,PUT /api/groups/group/{groupUID}andPOST /api/groups/group/{groupUID}/deleteanswer 403 for a non-administrator caller when teams are disabled for the workspace, with the reason in the response body. An administrator with write access is unaffected — turning teams off takes the feature away from users, it does not freeze the groups the workspace is built on. - Migration jobs (admin, write access).
POST /api/migrations/cancelcancels the run and waits up to 30 seconds for it to stop, so a caller can rewrite or reset the task straight afterwards. It answers 409 if the migration's own code never honours its cancellation token.POST /api/migrations/reset-task-statusanswers 409 while a job is still running, rather than throwing a500. - Synthetic sample downloads (any signed-in, non-guest user).
POST /api/node/download/synthetic/csvandPOST /api/node/download/synthetic/jsonlno longer take abaseURLquery parameter — the export now carries only the generated nodes' own fields and timestamp, with noLink,UID,TypeorEdgeCountcolumn. Drop the parameter from existing callers. SeeWithoutGraphMetadata().
User record field visibility
A _User node read by another signed-in user now returns only UID, Type, Timestamp, UserName, FirstName, LastName, FullName and Email. The administrative fields — the system-admin flag, roles, active, confirmed, one-time-password state, metadata and interface preferences — are withheld.
The projection applies to every generic reader:
GET /api/node/{uid}POST /api/node/queryPOST /api/node/many/fullPOST /api/node/many/field(which answersBLOCKEDfor a field the caller may not read, on the plain and the interpolated branch alike)GET /api/groups/group/{groupUID}/users- search results
- the CSV / JSON Lines / Excel downloads (
POST /api/node/download/csv,download/jsonl,download/zip)
The user themselves and any system administrator — read-only included — still read the whole record, as do the self-or-admin endpoints under /api/users/user/{uid}.
Two consequences for integrations:
- Exports previously bypassed field-level access control entirely. It is now applied per node, and a cell the reader may not see is blanked. An integration parsing those exports may see fewer populated columns than before.
- A user is always granted access to their own user node, so node-type access configuration cannot lock someone out of their own record.
Search
The search engine is reachable from custom endpoints via Graph.CreateSearchAsync / CreateSearchAsUserAsync. There is no public HTTP route for ad-hoc search — wrap your own retrieval logic in a custom endpoint instead. This makes it impossible to bypass scoping or ranking via raw HTTP, and gives you a stable contract you can version.
Graph
The graph is reachable via the Q() chain from inside custom endpoints, the admin Shell, and Curiosity.Library in connectors. As with search, raw graph traversal is not exposed over HTTP for safety; expose what you need via endpoints.
Versioning
The Workspace is delivered as a versioned container image using calendar versioning (curiosityai/curiosity:26.6.66474 — YY.M.build, so 26.6 is June 2026 and 66474 is the running build number). The built-in endpoints listed on this page are part of that contract: any breaking change is called out in the release notes and a migration note in the changelog for that release.
Your custom endpoints are versioned by you. The recommended pattern is to put the version in the endpoint name (similar-tickets-v2) and keep the old endpoint live during the deprecation window.
Where to go next
- API Usage — the practical guide with end-to-end examples.
- Custom Endpoints — write the server side.
- Token scopes — pick the right token for the caller.
- Error codes — what every status code means and how to react.