API Reference
Every HTTP endpoint the studio exposes, grouped by who calls it. The base URL is the studio's own
address β http://localhost:3000 in development.
Authentication
There are two identities in the platform and each has its own credential.
| Caller | Credential | Sent as |
|---|---|---|
| A person | The session cookie. | The cookie itself, sent by the browser on every request. |
| An agent | Its apiKey and its agentSecret. | In the request body of the REST calls, and in the socket payload of the auth request. |
A session is created by POST /api/auth/login and lasts seven days. The cookie is httpOnly and
sameSite="lax", and it carries the secure flag when NODE_ENV=production.
An agent holds two credentials, and each belongs to one channel. The apiKey is what the REST
endpoints under /api/agent take; the agentSecret is what the socket handshake compares. Neither is
ever sent in a header. See Socket Protocol for the socket side.
The web UI, the REST API and the realtime socket are served by the same server on the same port. Run the studio behind TLS, or on a trusted private network, and give every agent an account with the narrowest role that still lets it work.
Conventions
| Convention | Rule |
|---|---|
| Request bodies | JSON. Content-Type: application/json. |
| Response bodies | JSON, except the redirect from GET /api/auth/sso. |
| Identifiers | Opaque strings. agentId, commandId and the ids under /api/admin are not to be parsed. |
| Timestamps | ISO 8601, in UTC. |
| Errors | {"error": "β¦"}. POST /api/agent/register uses {"success": false, "error": "β¦"}. |
| Cursors | None. Collections are returned whole. |
Setup and system
{
"status": "ready",
"needsSetup": false,
"databaseConfigured": true,
"adminCreated": true,
"version": "1.0.0"
}status is uninitialized (no database configured), needs_admin (configured but no admin) or
ready. version is the platform's own version string β see
Version Compatibility.
{
"isComplete": true,
"currentStep": "complete",
"startedAt": "2026-10-21T07:30:00.000Z",
"needsResume": false
}The body selects the step:
{ "step": "license", "data": { "licenseKey": "ENT0-DKST-UDIO-2026" } }
{ "step": "database", "data": { "dbType": "sqlite", "database": "dkstudio" } }
{ "step": "initialize" }
{ "step": "admin", "data": { "email": "β¦", "name": "β¦", "password": "β¦", "confirmPassword": "β¦" } }
{ "step": "complete" }The initialize step applies the schema for the configured engine.
Optionally takes { "deleteDatabase": true } to drop the PostgreSQL or MySQL database. For SQLite
the database file is always deleted.
Authentication endpoints
{ "email": "admin@lab.example", "password": "β¦" }Answers with the user and sets session (httpOnly) and session_client.
{
"success": true,
"user": { "id": "β¦", "email": "β¦", "name": "β¦", "role": "admin" }
}{
"isAuthenticated": true,
"user": { "id": "β¦", "email": "β¦", "name": "β¦", "role": "admin" }
}Takes a code query parameter, redirects to the SSO provider at https://sso.ingeek.com, and sets
the auth_token and refresh_token cookies.
Agent-facing
These are the endpoints the agent calls.
Takes ?code=β¦ and answers with the licence's type, scope and whether it can still be used.
{
"code": "dk_l_β¦",
"name": "Bench 01",
"studioUrl": "http://localhost:3000",
"workstation": { "hostname": "bench-01", "platform": "linux", "arch": "x64" },
"capabilities": []
}Answers with the agent's identity:
{
"success": true,
"agentId": "β¦",
"apiKey": "β¦32 alphanumeric charactersβ¦",
"agentSecret": "β¦64 hex charactersβ¦",
"licenseType": "personal",
"scope": "personal",
"ownerUserId": "β¦",
"reRegistered": false
}message carries a human-readable summary of the outcome. Registering again with the agent's own
agentId and agentSecret updates the agent in place, sets reRegistered to true and increments
registrationCount.
| Status | Body | Meaning |
|---|---|---|
400 | {"success": false, "error": "β¦"} | A required field is absent. |
403 | invalid_code | The code matches no licence. |
403 | used | The licence was already spent on another machine. |
403 | revoked | The licence was revoked. |
403 | expired | The licence has passed its expiry. |
403 | invalid_credentials | The agentId and agentSecret pair matches no agent. |
{ "apiKey": "β¦" }{
"success": true,
"agentId": "β¦",
"name": "Bench 01",
"capabilities": []
}It does not change the agent's status β presence comes from the socket. An unknown key is 401 with
Invalid API key.
{ "agentId": "β¦", "status": "active" }Answers { "success": true, "timestamp": "β¦" }. Only last_seen_at is updated; the status field
in the body is ignored.
{ "agentId": "β¦" }Answers { "success": true }. The agent's stored status becomes unregistered and the agent row is
kept.
Returns the agent's pending commands and flips them to in_progress. Each command arrives as the
same command request envelope the socket carries:
{
"commands": [
{
"v": 1,
"type": "command",
"kind": "request",
"id": "β¦",
"timestamp": "β¦",
"payload": { "command": "shell", "args": { "script": "uname -a" } }
}
]
}The body is the command response envelope β the same one the socket carries:
{
"v": 1,
"type": "command",
"kind": "response",
"id": "β¦",
"timestamp": "β¦",
"payload": {
"ok": true,
"data": {
"status": "success",
"output": { "stdout": "β¦", "stderr": "", "exitCode": 0, "durationMs": 42 }
}
}
}Answers { "success": true }. The command row is marked completed and the result is stored as
JSON.
Studio
{
"agents": [
{ "id": "β¦", "name": "Bench 01", "status": "online", "lastSeen": "β¦", "capabilities": [] }
],
"timestamp": "β¦"
}Only agents whose stored status is active and that have a live socket are returned.
{
"agentId": "β¦",
"command": "shell",
"args": { "script": "uname -a", "cwd": "/tmp" },
"timeout": 30000
}Answers with a commandId:
{ "success": true, "commandId": "β¦", "message": "β¦" }| Status | Body | Meaning |
|---|---|---|
400 | Missing agentId or command | A required field is absent. |
400 | Agent is offline | The agent has no live socket. |
400 | Agent is not active | The agent's stored status is not active. |
404 | Agent not found | No agent has that id. |
{
"id": "β¦",
"agentId": "β¦",
"commandType": "shell",
"status": "completed",
"result": { "status": "success", "output": { "stdout": "β¦", "exitCode": 0 } },
"startedAt": "β¦",
"completedAt": "β¦"
}status is pending, in_progress or completed.
{
"totalProjects": 0,
"activeTestRuns": 0,
"devicesOnline": 0,
"successRate": 0
}Admin
{
"agents": [
{
"id": "β¦",
"name": "Bench 01",
"status": "active",
"connectionStatus": "online",
"capabilities": [],
"workstation": { "hostname": "bench-01" },
"lastSeen": "β¦",
"registrationCount": 1
}
]
}{
"userId": "β¦",
"licenses": [
{ "id": "β¦", "code": "dk_l_β¦", "type": "personal", "ownerUserId": "β¦", "status": "pending", "expiresAt": null }
],
"agents": []
}Takes { "type": "personal" | "lab", "location": "β¦", "label": "β¦", "expiresAt": "β¦" }. A personal
licence belongs to the caller; a lab licence can only be issued by an administrator. Answers with the
licence and its code β the only time the code is shown.
Answers with the agent's detail record.
Deletes the agent row and its command history. An id that matches no agent is 404 Agent not found.
Answers { "success": true } and sets the licence's status to revoked. The owner of a licence may
revoke their own; an administrator may revoke any. A licence that has already been consumed cannot
be revoked β the machine is on the ledger.
Administrators only. A licence that has already been consumed cannot be deleted.
{
"users": [
{ "id": "β¦", "email": "β¦", "name": "β¦", "role": "admin", "createdAt": "β¦" }
]
}{
"email": "engineer@lab.example",
"name": "Lab Engineer",
"password": "β¦",
"role": "engineer"
}Answers { "success": true, "user": { β¦ } }. role is admin, engineer or viewer; anything
unrecognised becomes viewer. The email must be unique.
Takes { "id": "β¦" } in the body and answers { "success": true }.
Status codes
| Status | Meaning |
|---|---|
200 | The request succeeded. |
400 | A required field is missing, or the agent cannot take the command. |
401 | Invalid API key β the key matches no agent. |
403 | The licence is invalid, revoked or expired. |
404 | No agent, licence or user has that id. |
500 | Database not configured β the endpoint needs the database and setup has not run. |
See also
- Command Reference β the commands these endpoints carry.
- Socket Protocol β the realtime counterpart to all of this.
- Configuration β the environment behind these endpoints.
- Error Codes β the status codes and error strings these endpoints return.