Loom exposes its full functionality via a versioned REST API.
All endpoints are rooted at /api/v1.
Authentication
All endpoints except POST /api/v1/login, the OAuth2 routes, /api/v1/health and the OpenAPI
document itself require a JWT bearer token:
Authorization: Bearer <token>
Obtain a token via the Authentication flow.
Base URL
http://<host>:8092/api/v1
Default port is 8092 (overridable via LOOM_SERVER_REST_PORT).
OpenAPI Specification
The complete API surface — every route, its path and query parameters, its request and response examples and the authentication it requires — is described by an OpenAPI 3.0 document. It is generated from the server’s endpoint registry, never written by hand, so it cannot drift away from the running code.
Download the spec
Feed it to a client generator, an API workbench (Postman, Insomnia, Bruno) or your editor's HTTP client.
Explore it
Browse the API by resource group in the explorer below — filter by keyword, expand an operation to see its parameters and examples, and link straight to it.
A running server serves the same document for its own endpoint set, with its own address filled in as the server URL:
curl http://localhost:8092/api/v1/openapi # YAML (default)
curl http://localhost:8092/api/v1/openapi.yaml # YAML
curl http://localhost:8092/api/v1/openapi.json # JSON
For a language Loom does not ship a client for, an OpenAPI generator will scaffold one from the specification:
openapi-generator-cli generate \
-i https://metaloom.io/docs/examples/openapi.yaml \
-g typescript-fetch -o ./loom-client
|
Note
|
The specification documents request and response bodies by example rather than by schema, so a generated client gives you the routes but leaves the bodies untyped. The maintained clients below do not have that limitation. |
|
Tip
|
Loom ships maintained, typed clients for Java and Python — see Java Client and Python Client. Prefer either over a generated one. |
API Explorer
The explorer below renders the bundled specification. Operations are grouped by resource; use the filter box to narrow the list, and expand an operation to see its parameters, request body and response examples.
To call a live server from here, set the server URL to your instance, click Authorize and paste a
JWT obtained from POST /api/v1/login or an API token from the
Loom UI admin area. Note that the API must allow your browser’s origin
(CORS) for in-page calls to succeed.
Listing, sorting and filtering
Every collection route (/assets, /collections, /users, …) answers with { data, _metainfo }
and accepts the same four query parameters.
| Parameter | Default | Description |
|---|---|---|
|
|
Page size. A bare |
|
— |
Seek cursor: the uuid to continue after. Take it from |
|
|
Column to order by — |
|
|
|
Primary keys are time-ordered (UUIDv7), so the default order is the order elements were created in: a new element appears at the end of the list rather than in an unpredictable position.
curl -H "Authorization: Bearer $TOKEN" \
"https://loom.example.com/api/v1/assets?sort=edited&dir=desc&limit=50"
Filtering
filter takes one or more terms in the form field[eq]=value, several of them separated by commas
in a single filter parameter. Terms are combined with AND.
| Field | Value | Available on |
|---|---|---|
|
exact name |
Collections, tags, libraries, persons, and assets (matched against the filename) |
|
user uuid |
Anything with a creator — assets, collections, tags, libraries, tasks, … |
|
collection uuid |
Assets — returns the members of that collection |
|
exact username |
Users |
|
byte range |
Assets |
|
state name |
Pipeline runs and run items |
curl -H "Authorization: Bearer $TOKEN" \
--data-urlencode "filter=collection[eq]=$COLLECTION_UUID,creator[eq]=$USER_UUID" \
--data-urlencode "sort=created" \
--get "https://loom.example.com/api/v1/assets"
|
Note
|
Filters match exactly — there is no "contains". For text search across the catalogue use the search endpoints, which rank by relevance. |
|
Warning
|
When you page a sorted listing, |
Endpoint Reference
The tables below are a reading guide to the most-used routes, grouped the way you are likely to need them. They are a summary — the API Explorer above is the complete and authoritative list.
Auth
| Method | Path | Description |
|---|---|---|
POST |
|
Login with username + password, returns a JWT and sets the session cookie |
GET |
|
Start the OAuth2 (BFF) flow — redirects to the identity provider |
GET |
|
Handle the identity provider callback and set the Loom JWT |
GET |
|
Clear the session cookie |
GET |
|
Load the currently authenticated user |
CRUD |
|
Manage long-lived API tokens |
Assets
| Method | Path | Description |
|---|---|---|
POST |
|
Create a new asset |
GET |
|
List assets (paged) |
POST |
|
Bulk-create assets |
POST |
|
Bulk-update assets |
GET |
|
Load asset by SHA-512 hash |
POST |
|
Update asset by SHA-512 hash |
DELETE |
|
Delete asset by SHA-512 hash |
GET |
|
Load asset by UUID |
POST |
|
Update asset by UUID |
DELETE |
|
Delete asset by UUID |
POST |
|
Add tag to asset |
PUT |
|
Add a whole set of tags at once, and remove the ones listed in |
DELETE |
|
Remove tag from asset, with every placement of it |
DELETE |
|
Remove one placement of a tag — one face, one timecode — keeping the others |
POST |
|
Add reaction |
GET |
|
List reactions |
GET |
|
Load reaction |
POST |
|
Update reaction |
DELETE |
|
Delete reaction |
POST |
|
Upload binary data |
GET |
|
Download binary data |
DELETE |
|
Delete binary data |
POST |
|
Upsert a generic JSON component (schema-agnostic node result sink) |
GET |
|
List an asset’s JSON components |
GET |
|
List typed asset components |
POST |
|
Record a node-result ledger row (asset, nodeKind, nodeId) |
|
Tip
|
json-comps + node-results is the lightweight persistence path used by Cortex nodes. See
Cortex Examples.
|
Users
| Method | Path | Description |
|---|---|---|
POST |
|
Create user |
GET |
|
List users (paged) |
GET |
|
Load user |
POST |
|
Update user |
DELETE |
|
Delete user |
Groups
| Method | Path | Description |
|---|---|---|
POST |
|
Create group |
GET |
|
List groups |
GET |
|
Load group |
POST |
|
Update group |
DELETE |
|
Delete group |
Roles
| Method | Path | Description |
|---|---|---|
POST |
|
Create role |
GET |
|
List roles |
GET |
|
Load role |
POST |
|
Update role |
DELETE |
|
Delete role |
Tags
| Method | Path | Description |
|---|---|---|
POST |
|
Create tag |
GET |
|
List tags |
GET |
|
Load tag |
POST |
|
Update tag |
DELETE |
|
Delete tag |
Pipelines
Pipelines are versioned: each update creates a new immutable version. Runs are executed by Loom’s engine and delegated to registered Cortex workers — see Pipeline Mechanism.
| Method | Path | Description |
|---|---|---|
POST |
|
Create pipeline (writes version 1) |
GET |
|
List pipelines |
GET |
|
Load pipeline (latest version) |
POST |
|
Update pipeline (creates a new version) |
DELETE |
|
Delete pipeline |
POST |
|
Start a run (body: |
GET |
|
List run history with status + counters |
GET |
|
List versions |
GET |
|
Load a specific version |
POST |
|
Restore a version as a new version |
GET |
|
WebSocket: live pipeline run events |
Processors (Cortex workers)
| Method | Path | Description |
|---|---|---|
GET |
|
List registered Cortex workers and their state |
GET |
|
Load one worker (capabilities, node-kind restriction) |
GET |
|
WebSocket: the control channel Cortex workers connect to |
AI Agent — Chats
| Method | Path | Description |
|---|---|---|
POST |
|
Create a chat |
GET |
|
List chats |
GET/POST/DELETE |
|
Load / update / delete a chat |
POST |
|
Send a message; receive the agent run as an SSE stream |
DELETE |
|
Cancel the active run |
See Chat & AI Agent for the streaming event protocol.
AI Agent — Sessions, Skills, Memory
| Method | Path | Description |
|---|---|---|
CRUD |
|
Publishable snapshots of a chat’s working state |
CRUD |
|
User-owned skill instruction packages |
GET |
|
Browse published skills |
POST |
|
Install (copy) a published skill |
GET |
|
List the caller’s memory scopes with usage + quota |
GET |
|
List notes of a scope |
GET/POST/PUT/DELETE |
|
Read / create / upsert / delete a memory note |
Libraries
| Method | Path | Description |
|---|---|---|
POST |
|
Create library |
GET |
|
List libraries |
GET |
|
Load library |
POST |
|
Update library |
DELETE |
|
Delete library |
Collections
| Method | Path | Description |
|---|---|---|
POST |
|
Create collection |
GET |
|
List collections |
GET |
|
Load collection |
POST |
|
Update collection |
DELETE |
|
Delete collection |
Problem Reports
/api/v1/failure-reports collects what users report as broken from the UI, joined to the server log
by the trace ID of the request that failed.
| Method | Path | Description |
|---|---|---|
|
|
Submit a report. Only |
|
|
List reports (paged) |
|
|
Load one report |
|
|
Download the attached screenshot |
|
|
Triage: |
|
|
Delete a report and its screenshot |
Submitting a report requires authentication and no permission. That is deliberate: a permission to
report a failure would, wherever it went ungranted, turn the one response to a breakage into a 403.
Reading, triaging and deleting need READ_FAILURE_REPORT, UPDATE_FAILURE_REPORT and
DELETE_FAILURE_REPORT respectively.
Only action — what was being attempted, in your own vocabulary — is mandatory. Everything
describing the failing request is optional, because failures that produced no response at all are
worth reporting too:
{
"action": "createPerson",
"traceId": "9f2c41ab7d0e4c6fa1b83e5d72c09148",
"httpMethod": "POST",
"path": "/api/v1/persons",
"statusCode": 500,
"errorMessage": "Internal Server Error",
"route": "/detection",
"text": "I filled in the name and pressed Create. The dialog closed but the person is not there.",
"screenshot": "data:image/png;base64,..."
}
A screenshot may be sent as a data URL or as bare base64, up to 5 MB decoded, and must be a PNG,
JPEG or WebP — the image type is determined from the bytes, not from what the request claims. An
oversized image answers 413. The userAgent recorded on a report is taken from the request headers
rather than the body, so it reflects what the server observed.
Further Endpoints
Additional endpoints exist for: attachments, tasks, comments, reactions,
embeddings, spaces, annotations, persons, clusters, blacklists, pools (asset pools),
tokens (API keys), notifications, me, health, and the pipeline node metadata under
pipeline/node-descriptors and pipeline/content-types. Most follow the same CRUD pattern under
/api/v1/<resource>.
Two of them deviate deliberately:
-
tasks/<uuid>/assignees—GETlists who a task is assigned to,POSTadds users and/or groups, and the twoDELETEsub-pathsassignees/users/<uuid>andassignees/groups/<uuid>remove one. -
notifications— the caller’s own inbox, and read-only apart from dismissal: there is no create route, because notifications are raised by the server when something happens.POST /notifications/<uuid>marks one read,POST /notifications/read-allmarks them all,DELETE /notifications/<uuid>dismisses one andDELETE /notificationsclears the inbox. Every route sees only your own entries; another user’s notification answers404.
Error Responses
All errors return the HTTP status code plus a JSON body carrying a human readable message and the
traceId of the request:
{
"message": "Cannot find asset with uuid 'abc'",
"traceId": "9f2c41ab7d0e4c6fa1b83e5d72c09148"
}
Every response — successful or not — also carries the same value in an X-Trace-Id header, and the
server writes it into the log line for the failure. Quote it when reporting a problem and the exact
request can be found with certainty rather than by guessing from timestamps. If your client already
has an identifier of its own, send it as X-Trace-Id and it will be used, provided it is at most 64
characters of letters, digits, -, _ or .; anything else is replaced with a freshly generated
one.
| Code | Meaning |
|---|---|
400 |
Validation error, or a malformed path/query parameter |
401 |
Missing, expired or invalid token |
403 |
The user lacks the permission the operation requires |
404 |
No such resource |
409 |
Conflict — e.g. a chat run is already active, or a memory entry already exists |
500 |
Internal server error |
503 |
No processor available to accept a pipeline run |