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:

MethodHeaderFormat
API Key (recommended)x-api-keyck_live_xxxxxxxxxxxxx — workspace-scoped at creation
Auth Tokenx-auth-tokenSession token from mobile app sign-in
Workspace overridex-workspace-idOptional UUID — session callers only

See Authentication for details, including the External Agents device-code flow for pairing external CLIs.

Request Format

  • Content-Type: application/json for POST/PUT/PATCH requests
  • Accept: application/json for 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" }
  }
}
FieldNotes
error.codeMachine-stable. This is what you branch on. See Error Codes.
error.messageHuman, English, developer-facing. May be reworded or translated at any time — never match on it.
error.detailsOptional 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 error when a client needs to branch on it — e.g. isRunning on a skill-install conflict. It is not inside details because that is exactly the field the client reads.

Common Endpoints

Skills (Public)

MethodEndpointDescription
GET/api/v1/marketplace/skillsList marketplace skills
GET/api/v1/marketplace/skills/categoriesList skill categories
GET/api/v1/marketplace/skills/:slugGet skill details

Skills (User)

MethodEndpointDescription
GET/api/v1/skillsList user’s installed skills
POST/api/v1/skills/:slug/installInstall a skill
GET/api/v1/skills/:slug/versionsVersion history, newest first
POST/api/v1/skills/:slug/versions/:versionNumber/restoreRestore a version (non-destructive: creates a new version)
POST/api/v1/skills/:slug/uninstallUninstall a skill
POST/api/v1/skills/:slug/runExecute 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

MethodEndpointDescription
GET/api/v1/artifactsList artifacts
GET/api/v1/artifacts/:idGet artifact details
GET/api/v1/artifacts/:id/downloadDownload artifact
DELETE/api/v1/artifacts/:idDelete artifact

Uploads

MethodEndpointDescription
POST/api/v1/uploads/signMint 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

MethodEndpointDescription
POST/api/v1/executionsCreate idle execution (Sessions API step 1)
GET/api/v1/executionsList execution history
GET/api/v1/executions/limitCheck execution limit
GET/api/v1/executions/:id/statusGet execution status
GET/api/v1/executions/:id/stream-tokenMint a signed SSE URL for a single execution (Sessions API step 2)
POST/api/v1/executions/:id/eventsSend user.message / user.interrupt (Sessions API step 3)
POST/api/v1/executions/:id/cancelCancel an active execution
POST/api/v1/executions/:id/rerunRerun a completed execution

User Profile & API Keys

MethodEndpointDescription
GET/api/v1/profileGet user profile
PATCH/api/v1/profileUpdate user profile
GET/api/v1/identitiesList user identities
GET/api/v1/api-keysList API keys
POST/api/v1/api-keysCreate API key
DELETE/api/v1/api-keys/:keyIdDelete API key
POST/api/v1/api-keys/podMint a pod key
DELETE/api/v1/api-keys/podRevoke own pod key
GET/api/v1/byokGet BYOK key status
PUT/api/v1/byokSet BYOK LLM API key
DELETE/api/v1/byokRemove 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.

MethodEndpointDescription
POST/api/v1/activateStart device flow — returns device_code, user_code, verification URL
POST/api/v1/activate/pollPoll 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

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”.

MethodEndpointDescription
POST/api/v1/agents/connectDeclare desired scopes — returns consent_id, link_code, poll_url
GET/api/v1/agents/connect/poll/:consentIdPoll 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-permissionRequest a single additional scope mid-conversation
GET/api/v1/agents/request-permission/poll/:requestIdPoll 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

MethodEndpointDescription
GET/api/v1/billing/balanceGet balance info
GET/api/v1/billing/packsList credit packs
POST/api/v1/billing/purchasePurchase credits
POST/api/v1/billing/subscribeSubscribe to Based Mode
POST/api/v1/billing/payment-methodPut a card on file (no charge)
POST/api/v1/billing/portalOpen the billing portal
GET/api/v1/billing/transactionsGet transaction history
GET/api/v1/billing/subscription/tierGet subscription tier
GET/api/v1/billing/subscription/infoGet subscription info
GET/api/v1/billing/publishable-keyGet billing publishable key

Workflows

MethodEndpointDescription
GET/api/v1/workflowsList workflows installed in the active workspace
GET/api/v1/workflows/:slugGet a single workflow, definition included
POST/api/v1/workflows/:slug/installInstall a JSON workflow definition
POST/api/v1/workflows/:slug/uninstallUninstall a workflow
DELETE/api/v1/workflows/:slugSame operation, addressed as a deletion
POST/api/v1/workflows/install-from-artifactInstall a workflow from a saved artifact
GET/api/v1/workflows/definitionsWhat this workspace can launch, and with what inputs
POST/api/v1/workflows/:id/launchStart a workflow run (accepts inputs); returns a runId
GET/api/v1/workflows/runsList workflow runs (manual and chat-started)
GET/api/v1/workflows/runs/:runIdGet workflow run details + step history
GET/api/v1/workflows/runs/:runId/eventsStream a run’s step-snapshot events over SSE
POST/api/v1/workflows/runs/:runId/cancelCancel a workflow run
POST/api/v1/workflows/runs/:runId/resumeResume a suspended workflow
GET/api/v1/workflows/queueList queued/active runs for the workspace
DELETE/api/v1/workflows/queue/:runIdRemove a run from the queue

Workspaces

MethodEndpointDescription
GET/api/v1/workspacesList user’s workspaces
POST/api/v1/workspacesCreate workspace
GET/api/v1/workspaces/currentGet current workspace
PATCH/api/v1/workspaces/:workspaceIdUpdate workspace
DELETE/api/v1/workspaces/:workspaceIdDelete workspace
GET/api/v1/workspaces/:workspaceId/membersList members
POST/api/v1/workspaces/:workspaceId/invitationsInvite a user
DELETE/api/v1/workspaces/:workspaceId/members/:userIdRemove a member
PATCH/api/v1/workspaces/:workspaceId/members/:userIdChange member role
POST/api/v1/workspaces/:workspaceId/leaveLeave a workspace
POST/api/v1/workspaces/:workspaceId/transferTransfer workspace ownership
POST/api/v1/workspaces/:workspaceId/github-org/syncSync GitHub org members
GET/api/v1/workspaces/:workspaceId/invitationsList pending invitations
DELETE/api/v1/workspaces/:workspaceId/invitations/:invitationIdRevoke invitation
GET/api/v1/invitations/:tokenPreview invitation
POST/api/v1/invitations/:token/acceptAccept invitation

Connectors

MethodEndpointDescription
GET/api/v1/connectors/availableList available connectors
GET/api/v1/connectors/listList configured connectors
GET/api/v1/connectors/statusGet all connector statuses
GET/api/v1/connectors/:type/auth-urlGet OAuth URL
GET/api/v1/connectors/:type/statusGet connector status
GET/api/v1/connectors/:type/configGet connector config
POST/api/v1/connectors/:type/disconnectDisconnect connector
GET/api/v1/connectors/:type/sourcesList connector sources
GET/api/v1/connectors/:type/sources/:repo/branchesList branches for a source

GitHub-specific (first-class)

MethodEndpointAuthDescription
GET/api/v1/connectors/github/confignoneIs the GitHub connector configured?
POST/api/v1/connectors/github/publishx-auth-tokenPublish workspace skills/agents/workflows to a user repo
POST/api/v1/connectors/github/create-prAPI key or sessionOpen a PR from execution diffs

Agents

MethodEndpointDescription
POST/api/v1/chat/:agentIdStream 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/streamReconnect 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/warmPre-warm the chat agent + DB connection ahead of the first turn. Fire-and-forget; never errors.
GET/api/v1/agentsList agents installed in the workspace
GET/api/v1/agents/:slugGet one installed agent
POST/api/v1/agents/installInstall an agent from marketplace ({slug}) or content ({slug,content})
POST/api/v1/agents/install-from-artifactInstall an agent from a saved artifact
DELETE/api/v1/agents/:slugUninstall an agent
GET/api/v1/marketplace/agentsBrowse marketplace agents
GET/api/v1/marketplace/agents/:slugMarketplace agent detail

Threads (Chat History)

Threads are conversation containers — chat messages live inside them.

MethodEndpointDescription
GET/api/v1/threadsList threads in the active workspace (optional apiKeyId filter)
POST/api/v1/threadsCreate a thread
GET/api/v1/threads/:threadIdGet a thread
DELETE/api/v1/threads/:threadIdDelete a thread
GET/api/v1/threads/:threadId/messagesList messages in a thread
POST/api/v1/threads/save-messagesBulk save messages

Workspace Memory

Working memory is a workspace-scoped Markdown document the agent reads and writes between turns.

MethodEndpointDescription
GET/api/v1/memoryRead workspace working memory
PUT/api/v1/memoryWrite workspace working memory

Secrets

Per-workspace encrypted credentials. Skills request them at execution time; the screen never shows the raw value.

MethodEndpointDescription
GET/api/v1/secretsList workspace secrets (names only)
POST/api/v1/secretsCreate or update a secret ({name, value})
PUT/api/v1/secrets/:idUpdate secret value
DELETE/api/v1/secrets/:idDelete a secret
GET/api/v1/skills/:skillId/secretsList the secret bindings for an installed skill
POST/api/v1/skills/:skillId/secretsBind a workspace secret to a skill env var
DELETE/api/v1/skills/:skillId/secrets/:bindingIdRemove a skill secret binding

Channels

MethodEndpointDescription
GET/api/v1/channelsWhatsApp 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.

MethodEndpointAuthDescription
POST/api/v1/searchsession onlyFull-text search across marketplace, library, artifacts, and memory

Activity — what CHANGED

The workspace change log. See Activity Endpoints.

MethodEndpointAuthDescription
GET/api/v1/activityAPI key or sessionChange log: object mutations, membership, approvals
GET/api/v1/activity/subscribe-urlAPI key or sessionSigned WebSocket URL for the live tail

Traces — what agents DID

Agent runs, turn by turn. See Traces Endpoints.

MethodEndpointAuthDescription
GET/api/v1/tracesAPI key or sessionList runs (filter by kind/status/range)
GET/api/v1/traces/:idAPI key or sessionOne run
GET/api/v1/traces/:id/eventsAPI key or sessionIts turns
POST/api/v1/traces/:id/eventsAPI key or sessionAppend turns (the update primitive)
POST/api/v1/traces/:id/continueAPI key or sessionGet 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.

MethodEndpointAuthDescription
GET/api/v1/statsAPI key or sessionSessions / sandbox / streak / library tiles
GET/api/v1/stats/dailyAPI key or sessionContribution heatmap series
GET/api/v1/workspace/model-policyAPI key or sessionRead the workspace model policy
PATCH/api/v1/workspace/model-policyAPI key or sessionUpdate it (owner/admin only)
GET/api/v1/workspace/clientssession only“Clients” tiles (one per active API key)
GET/api/v1/workspace/clients/:apiKeyIdsession onlyPer-client summary
POST/api/v1/workspace/clients/:apiKeyId/revokesession onlyRevoke an API key
GET/api/v1/workspace/content-languageAPI key or sessionLanguage the agent replies in (null = inherit)
PATCH/api/v1/workspace/content-languageAPI key or sessionSet 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.

MethodEndpointDescription
GET/api/v1/settings/languageAccount UI language — { "language": "en" }, null = device locale
PUT/api/v1/settings/languageSet or clear it — { "language": "es" } or { "language": null }
GET/api/v1/settings/default-modelYour account-level default model
PUT/api/v1/settings/default-modelSet it
GET/api/v1/modelsModels you may actually select (deployment allow-list ∩ workspace policy)
POST/api/v1/onboarding/completeMark onboarding done — idempotent, keeps the original timestamp

Audit Logs

MethodEndpointDescription
GET/api/v1/audit-logs?action=…Audit log entries (cursor pagination)

Bootstrap / Identity

MethodEndpointDescription
GET/api/v1/bootstrapCombined profile + workspaces payload — recommended single startup request
GET/api/v1/auth/configServer 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

MethodEndpointDescription
GET/api/v1/events/subscribe-urlSigned WebSocket URL for the signed-in user’s realtime channel — execution-state + cache-invalidation events

MCP

MethodEndpointAuthDescription
POST/mcp/x-api-keyMCP Streamable HTTP transport
GET/.well-known/mcp/server-card.jsonnoneMCP 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
ParameterTypeDefaultDescription
limitnumber20Max results per page
offsetnumber0Number of results to skip

SSE Streaming

Execution results stream over Server-Sent Events (SSE) using the Sessions API three-step flow:

  1. Create an idle execution — POST /api/v1/executions
  2. Connect to the live stream — GET /api/v1/executions/:id/stream-token returns a signed sseUrl; open it before step 3
  3. Send input to trigger the run — POST /api/v1/executions/:id/events with a user.message event

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.

CodeHTTP StatusDescription
UNAUTHORIZED401Invalid or missing auth
ACTIVATION_REQUIRED409The API key exists but has not been activated yet
FORBIDDEN403Authenticated, but lacks the role, scope or ownership
NOT_FOUND404Resource not found
INSUFFICIENT_CREDITS402Not enough credits
PAYMENT_METHOD_REQUIRED402No card on file — every plan needs one to run
LLM_KEY_REQUIRED402The workspace requires a provider key and none is set
INVALID_INPUT400Malformed or missing request data
VALIDATION_FAILED400A field failed validation — details names it
PAYLOAD_TOO_LARGE413An upload or archive is over its size budget
INVALID_CONNECTOR400Unknown or unsupported connector type
CONNECTOR_NOT_CONFIGURED400Connector not configured on this platform
CONNECTOR_NOT_CONNECTED403User has not connected this connector
ALREADY_EXISTS409The resource is already there
CONFLICT409The request conflicts with the resource’s state
EXECUTION_RUNNING429You already have an execution running
EXPIRED410Invitation, device code, or retired endpoint
RATE_LIMITED429Too many requests
EXECUTION_FAILED500The run itself failed
INTERNAL_ERROR500Server error
SERVICE_UNAVAILABLE502/503/504A 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"