REST API Overview
The clanker REST API provides programmatic access to skills, artifacts, executions, and billing.
Base URL
https://next.clanker.engineer/api
Authentication
All API requests require authentication via one of:
| Method | Header | Format |
|---|---|---|
| API Key (recommended) | x-api-key | ck_live_xxxxxxxxxxxxx — workspace-scoped at creation |
| Auth Token | x-auth-token | Session token from mobile app sign-in |
| Workspace override | x-workspace-id | Optional UUID — session callers only |
See Authentication for details, including the External Agents device-code flow for pairing external CLIs.
Request Format
- Content-Type:
application/jsonfor POST/PUT/PATCH requests - Accept:
application/jsonfor all requests
curl https://next.clanker.engineer/api/v1/marketplace/skills \
-H "x-auth-token: YOUR_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json"
Response Format
Success Response
{
"items": [ ... ],
"total": 42,
"limit": 20,
"offset": 0,
"hasMore": true
}
Or for simple responses:
{
"success": true,
"message": "..."
}
Error Response
Every error is the same envelope:
{
"error": {
"code": "NOT_FOUND",
"message": "Skill not found",
"details": { "field": "slug", "rule": "required" }
}
}
| Field | Notes |
|---|---|
error.code | Machine-stable. This is what you branch on. See Error Codes. |
error.message | Human, English, developer-facing. May be reworded or translated at any time — never match on it. |
error.details | Optional structured context, e.g. { field, rule } for validation failures. |
Two shapes are deliberately different and worth knowing about:
- Some responses carry an extra top-level sibling next to
errorwhen a client needs to branch on it — e.g.isRunningon a skill-install conflict. It is not insidedetailsbecause that is exactly the field the client reads.
Common Endpoints
Skills (Public)
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/marketplace/skills | List marketplace skills |
| GET | /api/v1/marketplace/skills/categories | List skill categories |
| GET | /api/v1/marketplace/skills/:slug | Get skill details |
Skills (User)
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/skills | List user’s installed skills |
| POST | /api/v1/skills/:slug/install | Install a skill |
| GET | /api/v1/skills/:slug/versions | Version history, newest first |
| POST | /api/v1/skills/:slug/versions/:versionNumber/restore | Restore a version (non-destructive: creates a new version) |
| POST | /api/v1/skills/:slug/uninstall | Uninstall a skill |
| POST | /api/v1/skills/:slug/run | Execute a skill (interactive use only) |
Note: Direct skill execution is for interactive use (dashboard, MCP/IDE). For bots, CI/CD, and all external integrations, use Workflows — they provide durable execution, automatic queueing, and crash recovery.
Artifacts
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/artifacts | List artifacts |
| GET | /api/v1/artifacts/:id | Get artifact details |
| GET | /api/v1/artifacts/:id/download | Download artifact |
| DELETE | /api/v1/artifacts/:id | Delete artifact |
Uploads
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/uploads/sign | Mint a short-lived signed URL to upload a file directly |
The body takes sizeBytes (required: the exact size of the file in bytes, at
most 25 MB) and an optional contentType. The file is sent with a PUT to the
returned upload.url using the returned upload.headers, and the body must be
exactly sizeBytes long — storage refuses any other length. The URL is valid
for 300 seconds; finish with POST /api/v1/install using the returned
upload.key.
Minting is rate-limited per workspace, by number of URLs per hour and by bytes
per day, with smaller allowances for an account without a card on file. Over
quota is 429 RATE_LIMITED with a Retry-After header; a missing, zero or
oversize sizeBytes is 400 VALIDATION_FAILED; and 503 SERVICE_UNAVAILABLE
means uploads are temporarily unavailable, so retry shortly.
Executions
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/executions | Create idle execution (Sessions API step 1) |
| GET | /api/v1/executions | List execution history |
| GET | /api/v1/executions/limit | Check execution limit |
| GET | /api/v1/executions/:id/status | Get execution status |
| GET | /api/v1/executions/:id/stream-token | Mint a signed SSE URL for a single execution (Sessions API step 2) |
| POST | /api/v1/executions/:id/events | Send user.message / user.interrupt (Sessions API step 3) |
| POST | /api/v1/executions/:id/cancel | Cancel an active execution |
| POST | /api/v1/executions/:id/rerun | Rerun a completed execution |
User Profile & API Keys
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/profile | Get user profile |
| PATCH | /api/v1/profile | Update user profile |
| GET | /api/v1/identities | List user identities |
| GET | /api/v1/api-keys | List API keys |
| POST | /api/v1/api-keys | Create API key |
| DELETE | /api/v1/api-keys/:keyId | Delete API key |
| POST | /api/v1/api-keys/pod | Mint a pod key |
| DELETE | /api/v1/api-keys/pod | Revoke own pod key |
| GET | /api/v1/byok | Get BYOK key status |
| PUT | /api/v1/byok | Set BYOK LLM API key |
| DELETE | /api/v1/byok | Remove BYOK key |
A pod key is a narrowly scoped key for a development pod’s coding agents. An
OAuth app holding the apikey:pod scope can mint one for its consenting user,
and the key can revoke itself; see
Agent Permissions.
Device Activation (External Agents)
For agents that need a fresh API key paired through the mobile app’s device-flow (8-char XXXX-XXXX user code). RFC 8628 device authorization — the API key is workspace-bound at issuance.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/activate | Start device flow — returns device_code, user_code, verification URL |
| POST | /api/v1/activate/poll | Poll for approval — returns api_key when approved |
| GET | /api/v1/activate/lookup | (Mobile/web) lookup pending-code metadata to render the approval UI |
| POST | /api/v1/activate/confirm | (Mobile/web) approve device code, mint API key |
| POST | /api/v1/activate/deny | (Mobile/web) deny device code |
Agent Permissions (scoped consent)
For agents that already hold an API key and want to declare the scope set they actually need — the user approves a scoped consent in the mobile app, and a per-request grant token is issued. Full details in /skill.md § “Agent Permissions”.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/agents/connect | Declare desired scopes — returns consent_id, link_code, poll_url |
| GET | /api/v1/agents/connect/poll/:consentId | Poll for approval — once-only emits grant_token + final scopes |
| POST | /api/v1/agents/connect/by-code | (Mobile) look up an activation by its link code |
| POST | /api/v1/agents/request-permission | Request a single additional scope mid-conversation |
| GET | /api/v1/agents/request-permission/poll/:requestId | Poll the escalation — emits grant_token once |
| DELETE | /api/v1/agents/:keyId/scopes/:scope | (Mobile/web) revoke a single scope from a key |
Grant tokens are sent via x-permission-grant: <token> on subsequent calls. Permission failures use the standard envelope — a credential missing a scope gets 403 with error.code = FORBIDDEN and a message naming the scope. Branch on the code, never on the message.
Billing
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/billing/balance | Get balance info |
| GET | /api/v1/billing/packs | List credit packs |
| POST | /api/v1/billing/purchase | Purchase credits |
| POST | /api/v1/billing/subscribe | Subscribe to Based Mode |
| POST | /api/v1/billing/payment-method | Put a card on file (no charge) |
| POST | /api/v1/billing/portal | Open the billing portal |
| GET | /api/v1/billing/transactions | Get transaction history |
| GET | /api/v1/billing/subscription/tier | Get subscription tier |
| GET | /api/v1/billing/subscription/info | Get subscription info |
| GET | /api/v1/billing/publishable-key | Get billing publishable key |
Workflows
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/workflows | List workflows installed in the active workspace |
| GET | /api/v1/workflows/:slug | Get a single workflow, definition included |
| POST | /api/v1/workflows/:slug/install | Install a JSON workflow definition |
| POST | /api/v1/workflows/:slug/uninstall | Uninstall a workflow |
| DELETE | /api/v1/workflows/:slug | Same operation, addressed as a deletion |
| POST | /api/v1/workflows/install-from-artifact | Install a workflow from a saved artifact |
| GET | /api/v1/workflows/definitions | What this workspace can launch, and with what inputs |
| POST | /api/v1/workflows/:id/launch | Start a workflow run (accepts inputs); returns a runId |
| GET | /api/v1/workflows/runs | List workflow runs (manual and chat-started) |
| GET | /api/v1/workflows/runs/:runId | Get workflow run details + step history |
| GET | /api/v1/workflows/runs/:runId/events | Stream a run’s step-snapshot events over SSE |
| POST | /api/v1/workflows/runs/:runId/cancel | Cancel a workflow run |
| POST | /api/v1/workflows/runs/:runId/resume | Resume a suspended workflow |
| GET | /api/v1/workflows/queue | List queued/active runs for the workspace |
| DELETE | /api/v1/workflows/queue/:runId | Remove a run from the queue |
Workspaces
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/workspaces | List user’s workspaces |
| POST | /api/v1/workspaces | Create workspace |
| GET | /api/v1/workspaces/current | Get current workspace |
| PATCH | /api/v1/workspaces/:workspaceId | Update workspace |
| DELETE | /api/v1/workspaces/:workspaceId | Delete workspace |
| GET | /api/v1/workspaces/:workspaceId/members | List members |
| POST | /api/v1/workspaces/:workspaceId/invitations | Invite a user |
| DELETE | /api/v1/workspaces/:workspaceId/members/:userId | Remove a member |
| PATCH | /api/v1/workspaces/:workspaceId/members/:userId | Change member role |
| POST | /api/v1/workspaces/:workspaceId/leave | Leave a workspace |
| POST | /api/v1/workspaces/:workspaceId/transfer | Transfer workspace ownership |
| POST | /api/v1/workspaces/:workspaceId/github-org/sync | Sync GitHub org members |
| GET | /api/v1/workspaces/:workspaceId/invitations | List pending invitations |
| DELETE | /api/v1/workspaces/:workspaceId/invitations/:invitationId | Revoke invitation |
| GET | /api/v1/invitations/:token | Preview invitation |
| POST | /api/v1/invitations/:token/accept | Accept invitation |
Connectors
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/connectors/available | List available connectors |
| GET | /api/v1/connectors/list | List configured connectors |
| GET | /api/v1/connectors/status | Get all connector statuses |
| GET | /api/v1/connectors/:type/auth-url | Get OAuth URL |
| GET | /api/v1/connectors/:type/status | Get connector status |
| GET | /api/v1/connectors/:type/config | Get connector config |
| POST | /api/v1/connectors/:type/disconnect | Disconnect connector |
| GET | /api/v1/connectors/:type/sources | List connector sources |
| GET | /api/v1/connectors/:type/sources/:repo/branches | List branches for a source |
GitHub-specific (first-class)
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/v1/connectors/github/config | none | Is the GitHub connector configured? |
| POST | /api/v1/connectors/github/publish | x-auth-token | Publish workspace skills/agents/workflows to a user repo |
| POST | /api/v1/connectors/github/create-pr | API key or session | Open a PR from execution diffs |
Agents
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/chat/:agentId | Stream a chat with a workspace-installed agent. Default seed gives every workspace clanka-01; the marketplace adds more. The agentId must match an installed slug. Response carries an x-clanker-run-id header for resumable reconnect. |
| GET | /api/v1/chat/:agentId/stream | Reconnect to a dropped turn. Query: runId (required, from the x-clanker-run-id response header), threadId, offset (chunks already received, default 0). Replays cached chunks, then resumes the live tail. |
| POST | /api/v1/chat/:agentId/warm | Pre-warm the chat agent + DB connection ahead of the first turn. Fire-and-forget; never errors. |
| GET | /api/v1/agents | List agents installed in the workspace |
| GET | /api/v1/agents/:slug | Get one installed agent |
| POST | /api/v1/agents/install | Install an agent from marketplace ({slug}) or content ({slug,content}) |
| POST | /api/v1/agents/install-from-artifact | Install an agent from a saved artifact |
| DELETE | /api/v1/agents/:slug | Uninstall an agent |
| GET | /api/v1/marketplace/agents | Browse marketplace agents |
| GET | /api/v1/marketplace/agents/:slug | Marketplace agent detail |
Threads (Chat History)
Threads are conversation containers — chat messages live inside them.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/threads | List threads in the active workspace (optional apiKeyId filter) |
| POST | /api/v1/threads | Create a thread |
| GET | /api/v1/threads/:threadId | Get a thread |
| DELETE | /api/v1/threads/:threadId | Delete a thread |
| GET | /api/v1/threads/:threadId/messages | List messages in a thread |
| POST | /api/v1/threads/save-messages | Bulk save messages |
Workspace Memory
Working memory is a workspace-scoped Markdown document the agent reads and writes between turns.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/memory | Read workspace working memory |
| PUT | /api/v1/memory | Write workspace working memory |
Secrets
Per-workspace encrypted credentials. Skills request them at execution time; the screen never shows the raw value.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/secrets | List workspace secrets (names only) |
| POST | /api/v1/secrets | Create or update a secret ({name, value}) |
| PUT | /api/v1/secrets/:id | Update secret value |
| DELETE | /api/v1/secrets/:id | Delete a secret |
| GET | /api/v1/skills/:skillId/secrets | List the secret bindings for an installed skill |
| POST | /api/v1/skills/:skillId/secrets | Bind a workspace secret to a skill env var |
| DELETE | /api/v1/skills/:skillId/secrets/:bindingId | Remove a skill secret binding |
Channels
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/channels | WhatsApp channel status for this deployment |
The inbound webhook (/api/webhooks/whatsapp/{agentId}, GET for Meta’s
verification challenge and POST for message events) is not listed above: it is
called by Meta, not by an API client, and authenticates with an HMAC-SHA256
signature rather than a Clanker credential. Inbound third-party callbacks live
under /api/webhooks/* and are deliberately unversioned — their URLs sit in
someone else’s console, so a version bump would be a coordinated external edit.
Search
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/v1/search | session only | Full-text search across marketplace, library, artifacts, and memory |
Activity — what CHANGED
The workspace change log. See Activity Endpoints.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/v1/activity | API key or session | Change log: object mutations, membership, approvals |
| GET | /api/v1/activity/subscribe-url | API key or session | Signed WebSocket URL for the live tail |
Traces — what agents DID
Agent runs, turn by turn. See Traces Endpoints.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/v1/traces | API key or session | List runs (filter by kind/status/range) |
| GET | /api/v1/traces/:id | API key or session | One run |
| GET | /api/v1/traces/:id/events | API key or session | Its turns |
| POST | /api/v1/traces/:id/events | API key or session | Append turns (the update primitive) |
| POST | /api/v1/traces/:id/continue | API key or session | Get the handle to continue it |
Workspace Dashboard
These are scoped by the x-workspace-id header (or the workspace baked into an
API key) — there is no workspace id in the path. See
Workspace scoping.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/v1/stats | API key or session | Sessions / sandbox / streak / library tiles |
| GET | /api/v1/stats/daily | API key or session | Contribution heatmap series |
| GET | /api/v1/workspace/model-policy | API key or session | Read the workspace model policy |
| PATCH | /api/v1/workspace/model-policy | API key or session | Update it (owner/admin only) |
| GET | /api/v1/workspace/clients | session only | “Clients” tiles (one per active API key) |
| GET | /api/v1/workspace/clients/:apiKeyId | session only | Per-client summary |
| POST | /api/v1/workspace/clients/:apiKeyId/revoke | session only | Revoke an API key |
| GET | /api/v1/workspace/content-language | API key or session | Language the agent replies in (null = inherit) |
| PATCH | /api/v1/workspace/content-language | API key or session | Set it (owner/admin only) |
Account Settings
Personal, account-level state — distinct from the workspace settings above. A transcript is shared, so the language the agent replies in is a workspace property; the language your UI renders in is yours.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/settings/language | Account UI language — { "language": "en" }, null = device locale |
| PUT | /api/v1/settings/language | Set or clear it — { "language": "es" } or { "language": null } |
| GET | /api/v1/settings/default-model | Your account-level default model |
| PUT | /api/v1/settings/default-model | Set it |
| GET | /api/v1/models | Models you may actually select (deployment allow-list ∩ workspace policy) |
| POST | /api/v1/onboarding/complete | Mark onboarding done — idempotent, keeps the original timestamp |
Audit Logs
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/audit-logs?action=… | Audit log entries (cursor pagination) |
Bootstrap / Identity
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/bootstrap | Combined profile + workspaces payload — recommended single startup request |
| GET | /api/v1/auth/config | Server auth capabilities (e.g. SSO enabled?) — no auth required |
| POST | /api/v1/push-token | (Session only) Register a device push token for mobile notifications |
Real-time Events
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/events/subscribe-url | Signed WebSocket URL for the signed-in user’s realtime channel — execution-state + cache-invalidation events |
MCP
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /mcp/ | x-api-key | MCP Streamable HTTP transport |
| GET | /.well-known/mcp/server-card.json | none | MCP server discovery document |
MCP traffic is served by the dedicated MCP edge worker, not by the REST backend. Point clients at
https://next.clanker.engineer/mcp/and the request is routed transparently.
Rate Limiting
API requests are rate-limited to 100 requests per minute per user. Skill execution endpoints are limited to 30 requests per minute. See Rate Limits for the rest, including how callers are identified.
Rate limit headers are included in responses:
x-ratelimit-limit: 100
x-ratelimit-remaining: 98
x-ratelimit-reset: 45
Pagination
List endpoints support pagination:
GET /api/v1/artifacts?limit=20&offset=0
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 20 | Max results per page |
offset | number | 0 | Number of results to skip |
SSE Streaming
Execution results stream over Server-Sent Events (SSE) using the Sessions API three-step flow:
- Create an idle execution —
POST /api/v1/executions - Connect to the live stream —
GET /api/v1/executions/:id/stream-tokenreturns a signedsseUrl; open it before step 3 - Send input to trigger the run —
POST /api/v1/executions/:id/eventswith auser.messageevent
The stream replays all history from the beginning, then delivers live events, and closes automatically when session.status_idle arrives.
See Executions API for event types, stop reasons, reconnect parameters, and the session-level stream.
Workflow Results
Launching a workflow (POST /api/v1/workflows/:id/launch) accepts only inputs and returns a runId immediately. The run is asynchronous — retrieve its outcome by polling the run or streaming it:
# Poll until terminal
curl https://next.clanker.engineer/api/v1/workflows/runs/run-1770000000000-8fq2k1 \
-H "x-api-key: ck_live_xxxxxxxxxxxxx"
# → { "run": { "status": "success", ... }, "steps": { "<stepId>": { ... } } }
Terminal statuses are success, failed and canceled; suspended means the
run is parked waiting for a human and resumes on request.
For live step events, open the SSE stream at GET /api/v1/workflows/runs/:runId/events (using the runId from launch). Workflows started from a chat thread also report completion back into that thread automatically.
See the Workflows API for full details.
Error Codes
These are the values of error.code. Codes are stable; the accompanying
error.message is not.
| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Invalid or missing auth |
ACTIVATION_REQUIRED | 409 | The API key exists but has not been activated yet |
FORBIDDEN | 403 | Authenticated, but lacks the role, scope or ownership |
NOT_FOUND | 404 | Resource not found |
INSUFFICIENT_CREDITS | 402 | Not enough credits |
PAYMENT_METHOD_REQUIRED | 402 | No card on file — every plan needs one to run |
LLM_KEY_REQUIRED | 402 | The workspace requires a provider key and none is set |
INVALID_INPUT | 400 | Malformed or missing request data |
VALIDATION_FAILED | 400 | A field failed validation — details names it |
PAYLOAD_TOO_LARGE | 413 | An upload or archive is over its size budget |
INVALID_CONNECTOR | 400 | Unknown or unsupported connector type |
CONNECTOR_NOT_CONFIGURED | 400 | Connector not configured on this platform |
CONNECTOR_NOT_CONNECTED | 403 | User has not connected this connector |
ALREADY_EXISTS | 409 | The resource is already there |
CONFLICT | 409 | The request conflicts with the resource’s state |
EXECUTION_RUNNING | 429 | You already have an execution running |
EXPIRED | 410 | Invitation, device code, or retired endpoint |
RATE_LIMITED | 429 | Too many requests |
EXECUTION_FAILED | 500 | The run itself failed |
INTERNAL_ERROR | 500 | Server error |
SERVICE_UNAVAILABLE | 502/503/504 | A dependency we proxy to is down — retrying may work |
This table is pinned against the server’s own ErrorCodes by
server/__tests__/api-docs-drift.test.ts — adding a code without a row here,
or leaving a row for a code that no longer exists, fails that test.
Statuses are the usual mapping, not a guarantee: a route may answer with a more specific status when it has one. Branch on the code and read the status as a hint, not the reverse.
SDK & Libraries
Currently, no official SDKs are available. We recommend using the MCP protocol with compatible AI clients, or making direct HTTP requests.
Example with fetch:
const response = await fetch("https://next.clanker.engineer/api/v1/marketplace/skills", {
headers: {
"x-auth-token": process.env.CLANKER_AUTH_TOKEN,
"Content-Type": "application/json",
},
});
const skills = await response.json();
Example with curl:
curl https://next.clanker.engineer/api/v1/marketplace/skills \
-H "x-auth-token: $CLANKER_AUTH_TOKEN"