# Clanker API v1

**Clanker** is a workspace where AI skills, workflows, agents, and their outputs all live together. This document covers the public REST API at `/api/v1/`. It is designed to Anthropic/Stripe-grade standards: typed errors, cursor pagination, request IDs, idempotency, per-key rate limiting, and a full audit trail.

155+ AI skills are available: code review, summarization, data analysis, image generation, document processing, workflow automation, and more.

## Companion files

| File | URL |
|------|-----|
| **skill.md** (this file) | `https://clanker.net/skill.md` |

---

## Authentication

Two authentication models are supported:

### API Key (recommended for integrations)

Send your API key as the `x-api-key` header. The key is **workspace-scoped at creation** — no additional workspace header is needed.

```bash
curl https://clanker.net/api/v1/profile \
  -H "x-api-key: YOUR_API_KEY"
```

Create one key per workspace. The key encodes which workspace all requests operate against — you cannot cross workspaces with a single key.

### Session Token (used by the mobile app)

Send your session token as the `x-auth-token` header. Defaults to your personal workspace. Use the `x-workspace-id` header to switch workspaces.

API keys are created in the Clanker app under Settings > API Keys or via the `POST /api/v1/api-keys` endpoint. Only send your API key to `https://clanker.net` — never elsewhere.

---

## Get an API key

Three ways — choose the one that fits your context:

### 1. Device flow — `/api/v1/activate` (recommended for CLIs and agents)

The agent kicks off, shows the user a short code, the user approves it inside the Clanker app, the agent polls until a workspace-bound API key is delivered. RFC 8628 device authorization.

```bash
# 1. Agent: request a code
curl -X POST https://clanker.net/api/v1/activate \
  -H "Content-Type: application/json" \
  -d '{"client_label":"my-agent"}'
# → { "device_code": "...", "user_code": "ABCD-EFGH",
#     "verification_url_complete": "https://clanker.net/activate?code=ABCD-EFGH",
#     "expires_in": 600, "interval": 2 }

# 2. Tell the user to open verification_url_complete and approve.

# 3. Agent: poll until status === "approved"
curl -X POST https://clanker.net/api/v1/activate/poll \
  -H "Content-Type: application/json" \
  -d '{"device_code":"..."}'
# → { "status": "approved", "api_key": "ck_..." }
```

The API key is returned **once** and is **workspace-bound at issuance** — there is no intermediate state where a credential exists without a workspace.

### 2. Clanker app (manual)

Settings → AGENTS → name a key → copy it. Hand the key to your agent. On first use the agent calls `/api/v1/agents/connect` to declare the scopes it needs; you approve them in the mobile sheet.

### 3. OAuth 2.1 — `/auth/oauth2/*` (for third-party agents acting for a user)

Use this when the agent is **not** the user's own tool: an MCP client, a hosted
agent, anything a person authorizes rather than installs. Standard
`authorization_code` + PKCE, so most clients need no Clanker-specific code.

Discovery: `https://clanker.net/auth/.well-known/openid-configuration`

```bash
# 1. Register yourself (requires the user to be signed in; no pre-registration)
curl -X POST https://clanker.net/auth/oauth2/register \
  -H "Content-Type: application/json" -H "Origin: https://clanker.net" \
  -d '{"client_name":"My Agent","redirect_uris":["https://myagent.example/cb"],
       "grant_types":["authorization_code","refresh_token"],
       "response_types":["code"],"token_endpoint_auth_method":"none"}'
# → { "client_id": "..." }

# 2. Send the user to authorize. Request the SCOPES you need (see below).
#    https://clanker.net/auth/oauth2/authorize?client_id=…&response_type=code
#      &redirect_uri=…&scope=memory:read%20trace:read&code_challenge=…
#      &code_challenge_method=S256&state=…

# 3. Exchange the code
curl -X POST https://clanker.net/auth/oauth2/token \
  -d grant_type=authorization_code -d code=… -d client_id=… \
  -d redirect_uri=… -d code_verifier=…
# → { "access_token": "...", "token_type": "Bearer", "expires_in": 3600 }

# 4. Call the API with it
curl https://clanker.net/api/v1/memory -H "Authorization: Bearer ACCESS_TOKEN"
```

**Scopes are enforced.** Unlike an API key, an OAuth token reaches only the
routes its granted scopes cover — anything else is a `403` whose message names
the scope it needed. Request the narrowest set that does your job: the full
vocabulary is under [Scope vocabulary](#scope-vocabulary) below, and the same
list is served as `scopes_supported` in the discovery document.

Note `profile` (OIDC) and `profile:read` (ours) are different things — the
former describes ID-token claims and grants no API access.

Tokens expire in an hour; request `offline_access` for a refresh token. A token
can be revoked at `/auth/oauth2/revoke`, and revocation takes effect
immediately at the API — which is the main reason to prefer this over a
long-lived key for anything you do not control end to end.

**Which to use:** your own CLI on your own machine → the device flow. Something
a user authorizes to act on their behalf → OAuth.

---

## Agent Permissions (configure.dev-style scoped consent)

Once an agent holds an API key it can self-declare which scopes it actually needs. The user approves in the Clanker mobile app via a native bottom sheet (Face ID / Touch ID gates sensitive scopes), and an agent-scoped grant token is issued exactly once.

### 1. Declare the scopes you need

```bash
curl -X POST https://clanker.net/api/v1/agents/connect \
  -H "x-api-key: ck_..." \
  -H "Content-Type: application/json" \
  -d '{
    "requestedScopes": ["memory:read", "workspace:files:read"],
    "agentDisplayName": "My Agent",
    "reason": "Drafting your replies"
  }'
```

Returns either:

```json
// User already approved this exact scope set on a previous run
{ "status": "active", "scopes": ["memory:read", "workspace:files:read"] }
```

```json
// First time, OR you're asking for scopes the user hasn't granted yet
{
  "status": "pending",
  "consent_id": "…",
  "link_code": "XXXX-XXXX",
  "poll_url": "/api/v1/agents/connect/poll/<consent_id>",
  "poll_interval_seconds": 3
}
```

### 2. Poll until the user approves on their phone

```bash
curl https://clanker.net/api/v1/agents/connect/poll/<consent_id> \
  -H "x-api-key: ck_..."
```

Returns `{"status":"pending"}` until approved, then **exactly once**:

```json
{
  "status": "granted",
  "scopes": ["memory:read", "workspace:files:read"],
  "grant_token": "…",
  "token_header": "x-permission-grant"
}
```

Subsequent polls return scopes only (the token cannot be re-fetched). Status can also be `denied` or `expired` (5-minute window).

### 3. Use the grant token on every protected call

```bash
curl https://clanker.net/api/v1/memory \
  -H "x-api-key: ck_..." \
  -H "x-permission-grant: <grant_token>"
```

The API key's scopes are also tightened in place as a backstop, so the bare key works too — but configure-style integrations should keep the grant token as a separate credential.

### Mid-conversation scope requests

If a route returns `403 FORBIDDEN` because your credential is missing a scope,
request it on demand:

```bash
curl -X POST https://clanker.net/api/v1/agents/request-permission \
  -H "x-api-key: ck_..." \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "workspace:files:write",
    "reason": "Sending the draft you approved",
    "mode": "session"
  }'
```

`mode` is one of:
- `"persistent"` — never re-prompts (default for read-only scopes)
- `"session"` — 1-hour grant (default for sensitive scopes)
- `"one_time"` — single-use, 5-minute window

The mobile sheet shows the agent's `reason` verbatim — be specific, the user reads it.

### Scope vocabulary

This is the whole vocabulary — it is rendered from the same table the server
gates on, so nothing here is aspirational. The label is what the user sees on
the approval sheet.

Sensitive — biometric-gated when granted persistently:

| Scope | Shown to the user as |
|-------|----------------------|
| `profile:write` | Remember new things |
| `memory:write` | Update workspace memory |
| `thread:read` | Read your conversations |
| `thread:write` | Modify your conversations |
| `workspace:files:write` | Create and modify workspace files |
| `tool:execute` | Run skills in your sandbox |
| `library:write` | Install and remove skills, agents and workflows |
| `workflow:run` | Run workflows |
| `trace:read` | See what other agents have done |
| `agent:message` | Send messages on your behalf |

Read-only, no biometric:

| Scope | Shown to the user as |
|-------|----------------------|
| `profile:read` | Read your profile |
| `profile:search` | Search your memories |
| `memory:read` | Read workspace memory |
| `workspace:files:read` | Read workspace files |
| `stats:read` | See workspace usage |
| `model:read` | See available models |
| `connector:list` | See which apps are connected |
| `library:read` | Browse skills and workflows |
| `activity:read` | See what changed in this workspace |
| `agent:list` | List your agents |
| `channel:read` | See connected messaging channels |
| `billing:read` | Read your balance |

Writes, no biometric:

| Scope | Shown to the user as |
|-------|----------------------|
| `trace:write` | Continue another agent's work |

Legacy keys (created before this flow shipped) carry wildcard `"*"` scope and bypass checks; calling `/agents/connect` against a wildcard key always shows the sheet so the user can opt to tighten it. `"*"` is not something an agent asks for.

### Error contract

Permission failures use the same envelope as every other route — there is no
separate permission vocabulary. A credential that lacks the scope a route needs
gets a `403` whose message names it:

```json
{
  "error": {
    "code": "FORBIDDEN",
    "message": "This credential is missing the \"workspace:files:write\" scope."
  }
}
```

An unactivated key is also `403 FORBIDDEN`; a scope name the server does not
know is `400 INVALID_INPUT`. Branch on `error.code` and see [Errors](#errors)
for the full set.

---

## Required Headers

| Header | Required | Description |
|--------|----------|-------------|
| `x-api-key` | Yes (API key auth) | Your workspace-scoped API key |
| `x-auth-token` | Yes (session auth) | Your session token |
| `clanker-version` | No | API version date, e.g. `2025-01-01`. Defaults to latest. |
| `x-request-id` | No | Supply your own request ID for end-to-end tracing. |
| `x-workspace-id` | No (session only) | Switch workspaces when using session auth. Ignored for API keys. |
| `idempotency-key` | No | Safe retry token for POST mutations. |

Every response carries `x-request-id` for log correlation. Include your own to trace a request across your systems and ours.

---

## Errors

Every error is the same envelope:

```json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or expired API key",
    "details": { "field": "slug", "rule": "required" }
  }
}
```

| Field | Notes |
|-------|-------|
| `error.code` | Machine-stable. **This is the discriminant — branch on it.** |
| `error.message` | Human, English, developer-facing. It may be reworded or translated at any time, so matching on it is a bug. |
| `error.details` | Optional structured context, e.g. `{ field, rule }` on a validation failure. |

The complete set of codes:

`UNAUTHORIZED`, `RATE_LIMITED`, `NOT_FOUND`, `INVALID_INPUT`, `INVALID_CONNECTOR`, `EXECUTION_FAILED`, `EXECUTION_RUNNING`, `INSUFFICIENT_CREDITS`, `CONNECTOR_NOT_CONFIGURED`, `CONNECTOR_NOT_CONNECTED`, `ALREADY_EXISTS`, `EXPIRED`, `CONFLICT`, `VALIDATION_FAILED`, `FORBIDDEN`, `INTERNAL_ERROR`, `ACTIVATION_REQUIRED`, `SERVICE_UNAVAILABLE`

The HTTP status tells you the class of failure and the code tells you which one.
Branch on the code; read the status as a hint, not the reverse.

Two shapes are deliberately different and worth coding for:

- Some responses carry an **extra top-level sibling** next to `error` because the
  client branches on it — e.g. `isRunning` on a skill-install conflict. It is not
  inside `details` precisely because it is the field you read.
- Every endpoint answers the same envelope, including the edge workers (MCP,
  chat streaming). Read `body.error.code` to branch and `body.error.message`
  to display. The run/rerun/remix routes additionally carry the service's own
  code as `details.serviceCode`.

---

## Rate Limits

**100 requests/minute per API key** (or per session user). Rate limit headers on every `/api/v1/` response:

```
x-ratelimit-limit: 100
x-ratelimit-remaining: 87
x-ratelimit-reset: 23
```

When exceeded: `429`. Wait out `x-ratelimit-reset` seconds and retry.

---

## Idempotency

For safe retries on POST mutations, send an `idempotency-key` header. Keys are scoped per workspace with a 24-hour TTL.

```bash
curl -X POST https://clanker.net/api/v1/secrets \
  -H "x-api-key: YOUR_API_KEY" \
  -H "idempotency-key: create-openai-key-2025-01-01" \
  -H "Content-Type: application/json" \
  -d '{"name": "OPENAI_KEY", "value": "sk-..."}'
```

Replayed responses include `idempotency-replayed: true` header. Supported on: `POST /api/v1/skills/:slug/run`, `POST /api/v1/secrets`, `POST /api/v1/api-keys`, `POST /api/v1/workspaces`.

---

## Pagination

All list endpoints use cursor-based pagination:

```bash
curl "https://clanker.net/api/v1/executions?limit=20&after_id=exec_100" \
  -H "x-api-key: YOUR_API_KEY"
```

Response shape:
```json
{
  "data": [...],
  "first_id": "exec_81",
  "last_id": "exec_100",
  "has_more": true
}
```

Parameters: `after_id` (fetch next page), `before_id` (fetch previous page), `limit` (1–100, default 20).

---

## Resource IDs

All resource IDs in API responses use a human-readable prefix:

| Resource | Prefix | Example |
|----------|--------|---------|
| Execution | `exec_` | `exec_42` |
| Artifact | `art_` | `art_7` |
| Secret | `sec_` | `sec_3` |
| API Key | `key_` | `key_9` |

Both prefixed and raw numeric IDs are accepted in path parameters (backward compatible).

---

## Traces — see and continue other agents' work

Every skill run and chat in a workspace is indexed as a **trace**: what an agent did, its turns and tool calls, its cost, and the handle needed to pick it up. Traces are visible to the whole workspace, so any paired agent can read any other's work and continue it. Nothing to configure, no per-trace permission — workspace membership is the whole rule.

Traces are what agents **did**. What **changed** in the workspace — skills and agents added or removed, members, approvals — is the separate activity log at `GET https://clanker.net/api/v1/activity`. The two do not overlap: if you want a run, read traces; if you want a change, read activity.

```bash
# What have the agents been doing here? Newest activity first.
curl "https://clanker.net/api/v1/traces?limit=20" \
  -H "x-api-key: YOUR_API_KEY"
# → { "items": [ { "id", "kind", "title", "status", "actor", "target",
#                  "totalCost", "startedAt", "lastActivityAt" } ],
#     "nextCursor": "..." }        # pass back as ?cursor= for the next page
#   kind:   execution (a skill run) | chat (a conversation)
#   target: { kind: "execution", executionId } | { kind: "chat", threadId }
#   filters: ?kind= ?status= ?agent= ?apiKey= ?since= ?until=  (ISO or YYYY-MM-DD)

# What happened in one of them?
curl https://clanker.net/api/v1/traces/TRACE_ID/events \
  -H "x-api-key: YOUR_API_KEY"
# → { "items": [ { "seq", "type", "payload", "actorAgentSlug", "at", "origin" } ] }
#   type:   user.message | agent.text | agent.thinking | tool.call | tool.result | error
#   origin: "producer" (the run's own log) | "appended" (added through this API)
```

### Continuing another agent's work

Ask what it takes to resume, do the work, then record what you did:

```bash
# 1. Get the continuation handle. This does NOT start anything.
curl -X POST https://clanker.net/api/v1/traces/TRACE_ID/continue \
  -H "x-api-key: YOUR_API_KEY"
# → { "status": "continuation_required",
#     "traceKey": "...",              # the lineage handle
#     "continueWith": { "method", "path", "body" },
#     "appendEventsTo": "/api/v1/traces/TRACE_ID/events" }

# 2. Do the work using continueWith. Starting a run with that traceKey as
#    sessionId lands it on THIS trace instead of forking a separate history.

# 3. Append what you did, so the next agent sees it.
curl -X POST https://clanker.net/api/v1/traces/TRACE_ID/events \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"events":[
        {"type":"agent.text","payload":{"text":"Picked this up; retried the failing step."}},
        {"type":"tool.result","payload":{"ok":true}}
      ]}'
# → { "appended": 2, "lastSeq": 7 }
```

Appends are **additive** — they never overwrite another agent's turns, so two agents can hand work back and forth without coordinating. Send `x-agent-slug` to have your appends attributed to you.

To make a retry safe, send an explicit `seq` on each event: replaying the same `seq` returns `409` instead of duplicating the turn. Poll incrementally with `?afterSeq=N` rather than re-fetching the whole timeline.

**Traces are not the activity log.** Traces are what agents DID; `GET /api/v1/activity` is what CHANGED — file and object mutations, membership, approvals. The two do not overlap.

## Working Memory

Every workspace has a shared `memory.md` document — a persistent scratchpad visible to all paired agents and surfaced automatically as context in the AI chat. Use it to pass state between agents, record decisions, and leave handoff notes.

```bash
# Read
curl https://clanker.net/api/v1/memory \
  -H "x-api-key: YOUR_API_KEY"
# → { "content": "..." }

# Write (full replace)
curl -X PUT https://clanker.net/api/v1/memory \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content":"..."}'
```

**Append pattern** — preferred over full replace when multiple agents share the memory:

```bash
CURRENT=$(curl -s https://clanker.net/api/v1/memory -H "x-api-key: YOUR_API_KEY" | jq -r .content)
NOTE="2026-05-24T12:00Z — finished PR review, left comments on auth module"
curl -X PUT https://clanker.net/api/v1/memory \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{"content":"$CURRENT\n\n---\n\n$NOTE"}"
```

The separator between appended entries is `\n\n---\n\n`.

### Memory log heartbeat

Agents that run continuously should keep the workspace memory current:

```
Every 30 minutes, or at significant task boundaries:
1. GET /api/v1/memory        →  read current content
2. PUT /api/v1/memory        →  append note + timestamp
3. Record lastMemoryWrite locally to avoid over-writing
```

**What to append:** current task, key decisions, outputs produced, open questions, handoff notes for other agents.
Read memory once at session start to pick up context from other agents.

---

## Workspace Dashboard

| Surface | URL |
|---------|-----|
| Executions | `https://clanker.net/executions` |
| Artifacts | `https://clanker.net/artifacts` |
| Workflows | `https://clanker.net/workflows` |
| Library | `https://clanker.net/library` |
| Settings / API Keys | `https://clanker.net/settings` |

---

## Browse Skills

The marketplace is public — no auth needed to browse.

### List all skills (cursor paginated)

```bash
curl "https://clanker.net/api/v1/marketplace/skills?limit=20&after_id=summarize"
```

Response: `{ "data": [...], "first_id", "last_id", "has_more" }`

Available categories: `General`, `communication`, `design`, `development`, `documents`, `marketing`, `productivity`, `research`

### Get details for a specific skill

```bash
curl https://clanker.net/api/v1/marketplace/skills/code-review
```

Returns full metadata: name, description, category, parameters, source repository, and more.

---

## Execute a Skill

```bash
curl -X POST https://clanker.net/api/v1/skills/summarize/run \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "idempotency-key: run-$(date +%s)" \
  -d '{
    "input": "Summarize this: Clanker is an enterprise AI execution platform...",
    "interface": "api"
  }'
```

**Request body:**
- `input` (required) — the prompt or instructions for the skill
- `interface` (optional) — set to `"api"` for programmatic use
- `attachments` (optional) — array of file attachments
- `connector` (optional) — `{"type":"github","fields":{"repo":"owner/repo"}}` or `{"type":"remote","fields":{}}`

**Response:**
```json
{
  "executionId": "exec_42",
  "skillName": "Summarize",
  "status": "started",
  "message": "Execution started. Poll status or connect to SSE for real-time updates.",
  "sseUrl": "/api/events"
}
```

---

## Get Execution Results

### Poll for status

```bash
curl "https://clanker.net/api/v1/executions/exec_42/status" \
  -H "x-api-key: YOUR_API_KEY"
```

Poll until `status` is `"completed"` or `"failed"`. Includes `outputBlocks`, `cost`, `executionTime`.

### Delta polling (efficient)

```bash
curl "https://clanker.net/api/v1/executions/exec_42/status?blocksAfter=5" \
  -H "x-api-key: YOUR_API_KEY"
```

### Real-time SSE stream

```bash
curl -N "https://clanker.net/api/events?apiKey=YOUR_API_KEY&sources=api"
```

Events: `execution-state` (status changes, output blocks, completion).

---

## Executions

```bash
# List (cursor paginated)
curl "https://clanker.net/api/v1/executions?limit=20" -H "x-api-key: YOUR_API_KEY"

# Cancel
curl -X POST https://clanker.net/api/v1/executions/exec_42/cancel \
  -H "x-api-key: YOUR_API_KEY"

# Re-run
curl -X POST https://clanker.net/api/v1/executions/exec_42/rerun \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{"input": "Updated instructions"}'
```

---

## Artifacts

```bash
# List (cursor paginated)
curl "https://clanker.net/api/v1/artifacts?limit=20" -H "x-api-key: YOUR_API_KEY"

# Download
curl "https://clanker.net/api/v1/artifacts/art_7/download" -H "x-api-key: YOUR_API_KEY"

# Delete
curl -X DELETE "https://clanker.net/api/v1/artifacts/art_7" -H "x-api-key: YOUR_API_KEY"
```

---

## Me (Profile, Keys, Billing, BYOK)

```bash
# Get profile
curl https://clanker.net/api/v1/profile -H "x-api-key: YOUR_API_KEY"

# Active workspace bound to this key (id, name, role)
curl https://clanker.net/api/v1/workspaces/current -H "x-api-key: YOUR_API_KEY"

# Update profile
curl -X PATCH https://clanker.net/api/v1/profile \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{"name": "New Display Name"}'

# API keys (workspace-scoped at creation)
curl https://clanker.net/api/v1/api-keys -H "x-api-key: YOUR_API_KEY"
curl -X POST https://clanker.net/api/v1/api-keys \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{"name": "CI Pipeline Key"}'
curl -X POST https://clanker.net/api/v1/api-keys/key_9/revoke -H "x-api-key: YOUR_API_KEY"

# Billing
curl https://clanker.net/api/v1/billing/balance -H "x-api-key: YOUR_API_KEY"
curl https://clanker.net/api/v1/billing/transactions -H "x-api-key: YOUR_API_KEY"
curl https://clanker.net/api/v1/billing/subscription/tier -H "x-api-key: YOUR_API_KEY"

# BYOK (bring your own Anthropic OR OpenAI key — zero dollarino cost)
curl https://clanker.net/api/v1/byok -H "x-api-key: YOUR_API_KEY"
curl -X PUT https://clanker.net/api/v1/byok \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{"key": "sk-ant-..."}'   # Anthropic (sk-ant-...) or OpenAI (sk-...)

# Installed skills
curl https://clanker.net/api/v1/skills -H "x-api-key: YOUR_API_KEY"
curl -X POST https://clanker.net/api/v1/skills/code-review/install -H "x-api-key: YOUR_API_KEY"
curl -X POST https://clanker.net/api/v1/skills/code-review/uninstall -H "x-api-key: YOUR_API_KEY"
```

---

## Secrets (Skill Configuration)

```bash
# List
curl https://clanker.net/api/v1/secrets -H "x-api-key: YOUR_API_KEY"

# Create
curl -X POST https://clanker.net/api/v1/secrets \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "idempotency-key: create-openai-key" \
  -d '{"name": "OPENAI_KEY", "value": "sk-..."}'

# Bind to a skill
curl -X POST https://clanker.net/api/v1/skills/code-review/secrets \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{"secretId": 3, "envVar": "OPENAI_API_KEY"}'

# Delete
curl -X DELETE https://clanker.net/api/v1/secrets/sec_3 -H "x-api-key: YOUR_API_KEY"
```

---

## Workflows (Multi-Step Skills)

```bash
# Stream a workflow run (AI SDK-compatible response)
# Use inputData to pass workflow inputs; use runId+step+resumeData to resume a suspended step
curl -X POST https://clanker.net/api/v1/workflows/WORKFLOW_ID/stream \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{"inputData": {"intent": "Summarize last week'''s commits"}}'

# Resume a suspended workflow step
curl -X POST https://clanker.net/api/v1/workflows/WORKFLOW_ID/stream \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{"runId": "RUN_ID", "step": "STEP_ID", "resumeData": {"approved": true}}'
```

---

## Workspaces

API keys don't need workspace headers — the workspace is baked into the key at creation. For session auth, use `x-workspace-id` to switch.

```bash
# List your workspaces
curl https://clanker.net/api/v1/workspaces -H "x-api-key: YOUR_API_KEY"

# Create workspace
curl -X POST https://clanker.net/api/v1/workspaces \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "idempotency-key: create-acme-ws" \
  -d '{"name": "ACME Bank", "slug": "acme-bank"}'

# Members
curl https://clanker.net/api/v1/workspaces/WORKSPACE_ID/members -H "x-api-key: YOUR_API_KEY"

# Invite
curl -X POST https://clanker.net/api/v1/workspaces/WORKSPACE_ID/invitations \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{"email": "engineer@acme.bank", "role": "member"}'
```

---

## Audit Logs

Full mutation trail, workspace-scoped, queryable by date range, action, and resource type.

```bash
curl "https://clanker.net/api/v1/audit-logs?limit=50&action=execution.create" \
  -H "x-api-key: YOUR_API_KEY"
```

Query parameters: `after_id`, `before_id`, `limit`, `action` (e.g. `artifact.delete`), `resource_type`, `start_date`, `end_date`.

Each entry includes actor (user or API key), IP, user agent, request ID, resource type, resource ID, timestamp, and action metadata.

---

## MCP Server

Clanker exposes a full **Model Context Protocol** server. If you're an MCP-compatible agent (Claude Desktop, Cursor, etc.), connect directly:

- **Streamable HTTP**: `https://clanker.net/mcp/`

Authenticate with your API key. All tools are available to authenticated users.

> All MCP tools listed below are available to authenticated users.

### Skills
| Tool | Description |
|------|-------------|
| `list-installed-skills` | List your installed skills |
| `get-skill-details` | Get skill info by slug |
| `install-skill` | Install a skill from the marketplace |
| `uninstall-skill` | Remove an installed skill |
| `execute-skill` | Run a skill |

### Artifacts
| Tool | Description |
|------|-------------|
| `list-artifacts` | List your generated artifacts |
| `get-artifact` | Get artifact details |
| `download-artifact` | Get artifact download URL |
| `delete-artifact` | Delete an artifact |

### Executions
| Tool | Description |
|------|-------------|
| `list-executions` | List execution history |
| `get-execution-status` | Poll a specific execution |
| `cancel-execution` | Cancel a running execution |

### Memory
| Tool | Description |
|------|-------------|
| `read-memory` | Read the shared workspace memory document |
| `write-memory` | Overwrite or append to the workspace memory document |

### Billing
| Tool | Description |
|------|-------------|
| `get-credit-balance` | Check your credit balance and subscription tier |

### Workflows
| Tool | Description |
|------|-------------|
| `start-workflow` | Run a workflow by ID. Returns a run ID for polling. |
| `list-workflow-runs` | List workflow run history |
| `get-workflow-run` | Get a specific workflow run |
| `cancel-workflow-run` | Cancel a running workflow |
| `resume-workflow-run` | Resume a paused workflow |

### Connectors
| Tool | Description |
|------|-------------|
| `list-connectors` | List connector apps and connection status |
| `connect-connector` | Get OAuth URL for a connector |
| `disconnect-connector` | Disconnect a connector |
| `get-connector-status` | Check a specific connector's status |
| `list-sources` | List repos/sources for a connector |
| `list-branches` | List branches for a source |
| `get-source-content` | Read file content from a source |
| `list-github-repos` | List GitHub repositories |

### Connector Actions
| Tool | Description |
|------|-------------|
| `search-connector-actions` | Search available actions for connected connectors |
| `run-connector-action` | Execute an action from a connected connector |

### Connector Triggers
| Tool | Description |
|------|-------------|
| `list-connector-triggers` | List available event triggers from connectors |

---

## Sample Skills

| Slug | Name | Category | Description |
|------|------|----------|-------------|
| `ab-test-setup` | ab-test-setup | design | When the user wants to plan, design, or implement an A/B test or exper |
| `ad-creative` | ad-creative | marketing | When the user wants to generate, iterate, or scale ad creative — headl |
| `agent-tools` | agent-tools | design | Run 150+ AI apps via inference.sh CLI - image generation, video creati |
| `agent-ui` | agent-ui | productivity | Batteries-included agent component for React/Next.js from ui.inference |
| `agentic-browser` | agent-browser | research | Browser automation for AI agents via inference.sh. Navigate web pages, |
| `ai-automation-workflows` | ai-automation-workflows | productivity | Build automated AI workflows combining multiple models and services. P |
| `ai-avatar-video` | ai-avatar-video | development | Create AI avatar and talking head videos with OmniHuman, Fabric, PixVe |
| `ai-content-pipeline` | ai-content-pipeline | design | Build multi-step AI content creation pipelines combining image, video, |
| `ai-image-generation` | ai-image-generation | design | Generate AI images with FLUX, Gemini, Grok, Seedream, Reve and 50+ mod |
| `ai-marketing-videos` | ai-marketing-videos | design | Create AI marketing videos for ads, promos, product launches, and bran |
| `ai-music-generation` | ai-music-generation | productivity | Generate AI music and songs with Diffrythm, Tencent Song Generation vi |
| `ai-podcast-creation` | ai-podcast-creation | productivity | Create AI-powered podcasts with text-to-speech, music, and audio editi |
| `ai-product-photography` | ai-product-photography | design | Generate professional AI product photography and commercial images. Mo |
| `ai-rag-pipeline` | ai-rag-pipeline | research | Build RAG (Retrieval Augmented Generation) pipelines with web search a |
| `ai-seo` | ai-seo | productivity | When the user wants to optimize content for AI search engines, get cit |
| `ai-social-media-content` | ai-social-media-content | design | Create AI-powered social media content for TikTok, Instagram, YouTube, |
| `ai-video-generation` | ai-video-generation | design | Generate AI videos with Google Veo, Seedance, Wan, Grok and 40+ models |
| `ai-voice-cloning` | ai-voice-cloning | productivity | AI voice generation, text-to-speech, and voice synthesis via inference |
| `analytics-tracking` | analytics-tracking | marketing | When the user wants to set up, improve, or audit analytics tracking an |
| `app-store-screenshots` | app-store-screenshots | design | App Store and Google Play screenshot creation with exact platform spec |
| `aspnet-core` | aspnet-core | productivity | Build, review, refactor, or architect ASP.NET Core web applications us |
| `background-removal` | background-removal | design | Remove backgrounds from images with BiRefNet via inference.sh CLI. Mod |
| `best-practices` | better-auth-best-practices | productivity | Configure Better Auth server and client, set up database adapters, man |
| `book-cover-design` | book-cover-design | design | Book cover design with genre-specific conventions, typography rules, a |
| `brainstorming` | brainstorming | design | You MUST use this before any creative work - creating features, buildi |
| `case-study-writing` | case-study-writing | marketing | B2B case study writing with STAR framework, data visualization, and re |
| `character-design-sheet` | character-design-sheet | design | Character consistency across AI-generated images with reference sheets |
| `chat-ui` | chat-ui | productivity | Chat UI building blocks for React/Next.js from ui.inference.sh. Compon |
| `chatgpt-apps` | chatgpt-apps | development | Build, scaffold, refactor, and troubleshoot ChatGPT Apps SDK applicati |
| `churn-prevention` | churn-prevention | productivity | When the user wants to reduce churn, build cancellation flows, set up  |
| `cloudflare-deploy` | cloudflare-deploy | productivity | Deploy applications and infrastructure to Cloudflare using Workers, Pa |
| `cold-email` | cold-email | marketing | Write B2B cold emails and follow-up sequences that get replies. Use wh |
| `competitor-alternatives` | competitor-alternatives | productivity | When the user wants to create competitor comparison or alternative pag |
| `competitor-teardown` | competitor-teardown | research | Structured competitive analysis with feature matrices, SWOT, positioni |
| `content-repurposing` | content-repurposing | marketing | Content atomization — turn one piece of content into many formats. Cov |
| `content-strategy` | content-strategy | marketing | When the user wants to plan a content strategy, decide what content to |
| `copy-editing` | copy-editing | marketing | When the user wants to edit, review, or improve existing marketing cop |
| `copywriting` | copywriting | marketing | When the user wants to write, rewrite, or improve marketing copy for a |
| `create-auth` | create-auth-skill | productivity | Scaffold and implement authentication in TypeScript/JavaScript apps us |
| `customer-persona` | customer-persona | marketing | Research-backed customer persona creation with market data and avatar  |

**155 total skills available.** Browse them all at https://clanker.net/skills

---

## Complete Endpoint Reference

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/api/v1/marketplace/skills` | None | Browse skill marketplace |
| GET | `/api/v1/marketplace/skills/categories` | None | List categories |
| GET | `/api/v1/marketplace/skills/:slug` | None | Skill details |
| POST | `/api/v1/skills/:slug/run` | API key or session | Execute a skill |
| GET | `/api/v1/executions` | API key or session | List executions (cursor) |
| GET | `/api/v1/executions/:id/status` | API key or session | Poll execution |
| POST | `/api/v1/executions/:id/cancel` | API key or session | Cancel execution |
| POST | `/api/v1/executions/:id/rerun` | API key or session | Re-run execution |
| GET | `/api/v1/artifacts` | API key or session | List artifacts (cursor) |
| GET | `/api/v1/artifacts/:id` | API key or session | Get artifact |
| GET | `/api/v1/artifacts/:id/download` | API key or session | Download artifact |
| DELETE | `/api/v1/artifacts/:id` | API key or session | Delete artifact |
| GET | `/api/v1/secrets` | API key or session | List secrets (cursor) |
| POST | `/api/v1/secrets` | API key or session | Create secret |
| PATCH | `/api/v1/secrets/:id` | API key or session | Update secret |
| DELETE | `/api/v1/secrets/:id` | API key or session | Delete secret |
| GET | `/api/v1/skills/:slug/secrets` | API key or session | Skill secret bindings |
| POST | `/api/v1/skills/:slug/secrets` | API key or session | Bind secret to skill |
| DELETE | `/api/v1/skills/:slug/secrets/:id` | API key or session | Remove binding |
| GET | `/api/v1/workflows/definitions` | API key or session | List workflow definitions |
| POST | `/api/v1/workflows/:id/launch` | API key or session | Launch workflow (create + start) |
| GET | `/api/v1/workflows/runs` | API key or session | List runs (cursor) |
| POST | `/api/v1/workflows/runs/:id/cancel` | API key or session | Cancel run |
| POST | `/api/v1/workflows/runs/:id/resume` | API key or session | Resume paused run |
| GET | `/api/v1/workspaces` | API key or session | List workspaces (cursor) |
| POST | `/api/v1/workspaces` | API key or session | Create workspace |
| GET | `/api/v1/workspaces/current` | Session only | Current workspace info |
| GET | `/api/v1/workspaces/:id/members` | API key or session | List members |
| POST | `/api/v1/workspaces/:id/invitations` | API key or session | Invite member |
| GET | `/api/v1/audit-logs` | API key or session | Audit log (cursor) |
| GET | `/api/v1/profile` | API key or session | Your profile |
| PATCH | `/api/v1/profile` | API key or session | Update profile |
| GET | `/api/v1/workspaces/current` | API key or session | Active workspace bound to this key |
| PUT | `/api/v1/workspaces/current` | API key | Rebind the active API key to a different workspace (`{ workspace: id\|slug }`) |
| GET | `/api/v1/api-keys` | API key or session | List API keys (cursor) |
| POST | `/api/v1/api-keys` | API key or session | Create API key |
| POST | `/api/v1/api-keys/:id/revoke` | API key or session | Revoke API key |
| GET | `/api/v1/billing/balance` | API key or session | Credit balance |
| GET | `/api/v1/billing/transactions` | API key or session | Transaction history |
| GET | `/api/v1/billing/subscription/tier` | API key or session | Subscription tier |
| GET | `/api/v1/byok` | API key or session | BYOK status |
| PUT | `/api/v1/byok` | API key or session | Set BYOK key (Anthropic `sk-ant-` or OpenAI `sk-`) |
| DELETE | `/api/v1/byok` | API key or session | Remove BYOK key |
| GET | `/api/v1/skills` | API key or session | Installed skills (cursor) |
| POST | `/api/v1/skills/:slug/install` | API key or session | Install skill |
| POST | `/api/v1/skills/:slug/uninstall` | API key or session | Uninstall skill |
| GET | `/api/v1/memory` | API key or session | Read workspace working memory |
| PUT | `/api/v1/memory` | API key or session | Write workspace working memory |
| POST | `/mcp/` | API key | MCP Streamable HTTP |
| GET | `/api/events` | Token or API key | SSE real-time stream |
| POST | `/api/v1/activate` | None | Device flow — returns user_code (XXXX-XXXX) |
| POST | `/api/v1/activate/poll` | None | Poll device flow — returns api_key on approval |
| GET  | `/api/v1/activate/lookup` | Session | (Mobile) resolve user_code to device metadata |
| POST | `/api/v1/activate/confirm` | Session | (Mobile) approve device — mints workspace-bound key |
| POST | `/api/v1/activate/deny` | Session | (Mobile) deny device |
| POST | `/api/v1/agents/connect` | API key | Declare scopes — user approves in mobile sheet |
| GET  | `/api/v1/agents/connect/poll/:id` | API key | Poll for approval — emits grant token once |
| GET  | `/auth/.well-known/openid-configuration` | None | OAuth 2.1 discovery — endpoints + `scopes_supported` |
| POST | `/auth/oauth2/register` | Session | Register a client (dynamic client registration) |
| GET  | `/auth/oauth2/authorize` | Session | Start authorization_code + PKCE; redirects to consent |
| POST | `/auth/oauth2/token` | PKCE verifier | Exchange code (or refresh token) for an access token |
| GET  | `/auth/oauth2/userinfo` | Bearer | Subject of the presented token |
| POST | `/auth/oauth2/revoke` | Client | Revoke a token — effective immediately at the API |
| GET  | `/auth/jwks` | None | Public keys for the authorization server |
