HTTP API
[object Object]
danio's own interface, the command line, the Blender add-on, plugins, and the devices that join a workspace all call the same HTTP API. Your own scripts can use it too.
Every endpoint is under /api/v1/ and speaks JSON. The parameters, responses and required permission of each endpoint are in the OpenAPI description; this page covers the conventions and the most-used endpoints.
Addresses
| Address | Who uses it | Token |
|---|---|---|
http://127.0.0.1:7421 | Programs on this computer. When the port is taken, danio moves up; the real address is shown under Settings → API | The local token |
| The host's workspace address, port 7420 by default | Devices and scripts on other computers. The host must have opened this port to other devices first | A workspace token |
For setting the port, see Configuration.
Tokens and permissions
Send the token with the request:
Authorization: Bearer <token>
| Token | What it can do | Where it comes from |
|---|---|---|
| Local token | Everything this computer's engine can do, including the endpoints that exist only locally | Shown under Settings → Local token; can be fixed with DANIO_TOKEN |
| Integration token | Only the access level you chose | The Plugins and integrations page in the workspace details, or danio workspace token add <name> --access <level> --integration |
| Device token | Used by a device to join the workspace and render; also carries an access level | Made when you add a device, see Tokens |
An integration token is shown only once, when it is made. Use Rotate to replace it; the old one stops working at once.
There are 9 workspace permissions, and an access level is a combination of them:
| Permission | What it allows |
|---|---|
workspace.view | View projects, tasks, progress, previews, devices and usage |
output.download | Download films and the fragments of shots |
job.submit | Submit tasks; update, pause, resume, cancel, re-render and delete the tasks you submitted |
job.manage | Control everyone's tasks, reorder the queue and change shot assignments |
project.edit | Create, change and delete projects |
device.manage | Add devices, approve join requests, change devices' access and settings, block |
storage.manage | Stores, resource packs, cleanup |
plugin.manage | Plugins and integration tokens |
workspace.manage | Workspace settings, backups, takeover |
The level None has no permissions, Viewer has the first two, Workstation adds job.submit and project.edit, and Admin has all of them.
In the OpenAPI description, each endpoint's x-required-permission names the one it needs. Besides the 9 above, there are four other values:
| Value | Meaning |
|---|---|
public | No token needed; used when a device joins |
token | Any request with a valid token |
device | Only a device token |
local | Only the local token |
When the permission is not enough, the answer is 403 with the error code forbidden, naming what is missing. GET /api/v1/me returns the identity and permissions of the current token.
Errors
On an error the answer is application/problem+json:
{
"type": "urn:danio:error:version-conflict",
"title": "Conflict",
"status": 409,
"detail": "…"
}
type is a stable error code for programs to act on; detail is for people, in the language of the request's Accept-Language: Chinese when the header is missing or starts with zh, English otherwise. Fields that failed validation are listed in errors; when a submission's checks fail, the reasons are listed in checks.
| Error code | Status | When |
|---|---|---|
invalid-request | 400 | The request itself is wrong |
unauthorized | 401 | No token, or an invalid one |
forbidden | 403 | Not enough permission, or this origin isn't allowed |
not-found | 404 | Not found |
conflict | 409 | Conflicts with the current state |
version-conflict | 409 | Someone updated the task first |
precondition-failed | 412 | If-Match didn't match |
validation-failed | 422 | A field failed validation |
check-failed | 422 | The submission's checks failed |
too-many-requests | 429 | Too many requests |
render-limit | 403 | As many devices are rendering as the workspace allows |
lease-ended | 409 | The chunk the device took has ended |
lease-taken | 409 | The chunk the device took went to another device |
internal | 500 | An error inside danio |
Retries and concurrency
- Submitting a task (
POST /api/v1/jobs), updating a task (POST /api/v1/jobs/{id}/versions) and comparing a draft (POST /api/v1/jobs/{id}/versions/draft) can carry anIdempotency-Key, a value you choose. Retrying with the same key within 24 hours returns the first result and doesn't register the task twice. Only successful results are remembered; a retry after a failure runs again. - The same three endpoints can carry
X-Danio-Request, an ID you choose. Thesubmit.progressevents carry it, so you can tell which ones are yours. - When updating a task, send
baseVersion. If a newer version exists, the answer is 409 with the error codeversion-conflict; compare again against the newest version. - Reading a project returns an
ETag. When changing it (PATCH /api/v1/projects/{id}), send it back inIf-Match. If someone changed it first, the answer is 412.
Lists
GET /api/v1/jobs and GET /api/v1/projects are paged: limit says how many items a page has, and the next in the answer goes into the next request's cursor. An empty next means the last page. The task list can also be filtered with project (a project ID), name (a task name, unique only within a project) and blend (a Blend file ID).
The {id} in a path accepts only IDs. To look something up by name, use a query parameter.
Event stream
GET /api/v1/events is a long-lived Server-Sent Events connection. It needs workspace.view:
curl -N -H "Authorization: Bearer $TOKEN" http://127.0.0.1:7421/api/v1/events
Each event has an id, an event (its type) and data (JSON). The id is a string; don't compare it as a number. The connection sends a : ping comment line every 15 seconds to keep itself alive.
When you reconnect, send the last Last-Event-ID you received and danio replays what you missed. It keeps the last 1024 events. When it can't replay (danio restarted, or you were away too long), it sends a reset event first; read the state again then.
The events listed in the OpenAPI description:
| Event | When |
|---|---|
submit.progress | Progress of a submission. The request field is the ID you gave in X-Danio-Request |
render.progress | Progress and state changes of rendering on this computer |
queue.changed | The queue changed: a task joined, or the order changed |
device.room | A device is low on disk space, full, or has recovered |
workspace.devices | The device list changed: a device joined or left, or its settings, name, access or block changed |
workspace.join-request | A device on the local network asked to join |
workspace.license | A license was imported or removed |
notice | A notice for this device's window program to show |
window.show | The danio window should open a page |
reset | The missed events can't be replayed; read the state again |
The stream carries other events too. Ignore any event type you don't know.
The OpenAPI description
The full description of the API is OpenAPI 3.1 JSON. You can get it in two ways:
# from the command line, in Chinese by default
danio openapi > openapi.json
# in English
danio openapi --lang en > openapi.json
# from a running danio, with any valid token
curl -H "Authorization: Bearer $TOKEN" \
"http://127.0.0.1:7421/api/openapi.json?lang=en" -o openapi.json
lang can be zh-CN or en. When it is missing, danio picks by Accept-Language, by the same rule as error messages.
In the command-line output, info.version is dev; a running danio gives its own version.
Under Settings → API, API reference opens the same description in the interface, where you can browse it.
Generic OpenAPI tools can generate a client from it, such as openapi-typescript and openapi-python-client.
Common endpoints
The permissions in the tables are the ones needed when you call with a workspace token. The local token has no limit. For the full list and the parameters, see the OpenAPI description.
Status and identity
| Endpoint | Permission | What it does |
|---|---|---|
GET /api/v1/health | token | Whether the engine is running; returns the version and start time |
GET /api/v1/me | token | The identity and permissions of the current token |
GET /api/v1/workspace | workspace.view | The workspace this computer hosts: name, settings, addresses it listens on |
Projects and tasks
| Endpoint | Permission | What it does |
|---|---|---|
GET /api/v1/projects | workspace.view | List projects |
POST /api/v1/projects | project.edit | Create a project |
GET /api/v1/projects/{id} | workspace.view | One project; returns an ETag |
PATCH /api/v1/projects/{id} | project.edit | Change a project's settings, with If-Match |
GET /api/v1/jobs | workspace.view | List tasks, newest first |
POST /api/v1/jobs | job.submit | Submit a task, one task per scene |
GET /api/v1/jobs/{id} | workspace.view | Task details |
DELETE /api/v1/jobs/{id} | job.submit | Delete a task |
POST /api/v1/jobs/{id}/pause | job.submit | Pause; rendered frames are kept |
POST /api/v1/jobs/{id}/resume | job.submit | Resume |
POST /api/v1/jobs/{id}/cancel | job.submit | Cancel |
POST /api/v1/jobs/{id}/versions/draft | job.submit | Compare against the newest version and make an update draft |
POST /api/v1/jobs/{id}/versions | job.submit | Update the task, making a new version |
GET /api/v1/jobs/{id}/preview | workspace.view | The preview of one frame, a JPEG; without frame, the newest frame |
GET /api/v1/jobs/{id}/films | workspace.view | The films of each version |
GET /api/v1/jobs/{id}/film | output.download | Download a film; without version, the current version; 404 when it isn't made yet |
Devices and tokens
| Endpoint | Permission | What it does |
|---|---|---|
GET /api/v1/devices | workspace.view | List devices |
PATCH /api/v1/devices/{id} | device.manage | Change the name or access, block or unblock |
DELETE /api/v1/devices/{id} | device.manage | Remove a device |
GET /api/v1/tokens | device.manage | List tokens |
POST /api/v1/tokens | device.manage | Make a token; the full token is returned only this once |
POST /api/v1/tokens/{id}/rotate | device.manage | Rotate an integration token |
DELETE /api/v1/tokens/{id} | device.manage | Revoke a token |
Plugins
| Endpoint | Permission | What it does |
|---|---|---|
GET /api/v1/plugins | plugin.manage | List plugins |
POST /api/v1/plugins | plugin.manage | Install a plugin; it is disabled after installing |
POST /api/v1/plugins/{id}/enable | plugin.manage | Enable, sending the permissions it declares |
POST /api/v1/plugins/{id}/disable | plugin.manage | Disable |
PUT /api/v1/plugins/{id}/settings | plugin.manage | Change settings |
GET /api/v1/jobs/{id}/handovers | workspace.view | How the film was handed to plugins |
POST /api/v1/jobs/{id}/handovers/{plugin}/retry | job.submit | Hand it to a plugin again |
Example
List tasks, then pause one:
curl -H "Authorization: Bearer $TOKEN" "http://192.168.1.20:7420/api/v1/jobs?limit=20"
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://192.168.1.20:7420/api/v1/jobs/<task ID>/pause
Replace 192.168.1.20:7420 with your host's workspace address.