REST API Overview
The clanker REST API provides programmatic access to skills, artifacts, executions, and billing.
Base URL
https://clanker.net/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://clanker.net/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 |
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 |
| GET | /api/v1/byok | Get BYOK key status |
| PUT | /api/v1/byok | Set BYOK LLM API key |
| DELETE | /api/v1/byok | Remove BYOK key |
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 |
| 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 |
| 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 |
| GET | /api/v1/threads/:threadId/members | Snapshot of clients currently active in the thread (presence) |
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://clanker.net/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.
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://clanker.net/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 |
INVALID_INPUT | 400 | Malformed or missing request data |
VALIDATION_FAILED | 400 | A field failed validation — details names it |
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 | Another execution is active in this workspace |
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://clanker.net/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://clanker.net/api/v1/marketplace/skills \
-H "x-auth-token: $CLANKER_AUTH_TOKEN"