CitationBenchTalk to Sales
API referenceAgent

Agent invoke API — universal entry point for running CitationBench skills

POST /v1/agent/invoke is the universal entry point for running the CitationBench agent. Pick a skill, pass its input, get an invocation handle to stream events, approve, cancel, or read results.

The universal entry point for running the CitationBench agent. Pick a skill from the registry, pass its input, get back an invocation handle. The handle lets you stream events, approve pauses, cancel, and read the final result.

CitationBench has one agent that loads skills on demand. bootstrap_brand, link_hunter, rank_monitor — these are skills, not separate agents. See Concept · Agent for the model.

Conceptual overview

An invocation is one durable run of one skill (which may chain into child invocations for sub-skills). It has:

  • a unique invocationId (CUID)
  • an agentId (CUID) — the specific agent instance that ran
  • a status state machine: PENDINGRUNNING → (WAITING_APPROVAL | WAITING_INPUT | WAITING_CHILDREN) → SUCCEEDED | FAILED | CANCELLED
  • a graph of child invocations (when the agent composes sub-skills)
  • a structured result on success, plus raw (agent narration) and files (paths the agent wrote)
  • a stream of events you can subscribe to

You can run an invocation FOREGROUND (synchronous, the response includes the result when ready) or BACKGROUND (the response is immediate; you poll or subscribe). Most agency workflows use BACKGROUND.

→ Concept: Agent (read first if you haven't)

Endpoints

MethodPathPurpose
POST/v1/agent/invokeStart an invocation
GET/v1/agent/invocations/{id}Get current state
GET/v1/agent/invocations/{id}/eventsSSE stream of events
GET/v1/agent/invocations/{id}/treeFull parent–child graph
POST/v1/agent/invocations/{id}/cancelCancel a running invocation
POST/v1/agent/invocations/{id}/continueMulti-turn continuation (chat skills)
GET/v1/agent/invocationsList recent invocations
GET/v1/agent/skillsList available skills

POST /v1/agent/invoke

Request

POST /v1/agent/invoke HTTP/1.1
Host: api.citationbench.com
Authorization: Bearer sk_live_***
X-Workspace-Id: ws_acme
Content-Type: application/json
Idempotency-Key: 7f3a1b18-7d8b-4f3e-9c4b-2c1a3e0a9b8f

{
  "skill": "bootstrap_brand",
  "input": {
    "domain": "acme.com",
    "depth": "thorough",
    "options": {
      "competitorCount": 8,
      "keywordTarget": 1000
    }
  },
  "mode": "BACKGROUND",
  "approval": {
    "required": true,
    "scope": "publish_or_outreach"
  },
  "budget": {
    "maxCredits": 200,
    "maxLlmCalls": 80
  },
  "files": ["seed-docs/competitor-brief.pdf"],
  "tags": ["onboarding", "client:acme"]
}

Parameters

FieldTypeRequiredDefaultNotes
skillstringyesSkill slug (built-in like bootstrap_brand, or custom:<slug> for a workspace-scoped one). Loaded by the generic agent at the start of the invocation.
inputobjectyesValidated against the skill's inputSchema (JSON Schema). Discover the schema via GET /v1/agent/skills/{slug}.
mode"FOREGROUND" | "BACKGROUND"noBACKGROUNDFOREGROUND blocks until done. Max 60 s; longer skills must use BACKGROUND.
approval.requiredbooleannoworkspace policyIf true, the agent pauses at outside-world steps
approval.scopeenumnoall_writespublish_or_outreach, all_writes, every_step
budget.maxCreditsnumbernoskill defaultHard cap; invocation fails if exceeded
budget.maxLlmCallsnumbernoskill defaultSame — caps the LLM-call loop
filesstring[]noFile paths (see Agent · files). Made available to the agent as readable inputs.
tagsstring[]no[]Free-text tags for filtering invocations later
parentInvocationIdstringnoSets this invocation as a child of another
sessionIdstringnoFor chat skills: continues an existing session

Response — BACKGROUND mode

HTTP/1.1 202 Accepted
Content-Type: application/json
X-Request-Id: req_01HVZ...

{
  "invocationId": "inv_01HVZRJZ3T8M7E0B0V0Z6KQ3A4",
  "sessionId":    "sess_01HVZRJZ3T8M7E0B0V0Z6KQ3A5",
  "agentId":      "agt_01HVZRJZ3T8M7E0B0V0Z6KQ3A6",
  "skill":        "bootstrap_brand",
  "status":       "PENDING",
  "mode":         "BACKGROUND",
  "workspace":    "ws_acme",
  "estimatedCost": {
    "credits": 152,
    "durationSeconds": 1200
  },
  "createdAt": "2026-05-24T08:00:00Z",
  "links": {
    "self":      "https://api.citationbench.com/v1/agent/invocations/inv_01HVZ...",
    "events":    "https://api.citationbench.com/v1/agent/invocations/inv_01HVZ.../events",
    "tree":      "https://api.citationbench.com/v1/agent/invocations/inv_01HVZ.../tree",
    "cancel":    "https://api.citationbench.com/v1/agent/invocations/inv_01HVZ.../cancel",
    "approvals": "https://api.citationbench.com/v1/agent/approvals?invocationId=inv_01HVZ..."
  }
}

Response — FOREGROUND mode

When the agent finishes within the 60 s limit, the response is the same shape as the BACKGROUND mode + a populated result, raw, files, and agentId.

{
  "invocationId": "inv_01HVZ...",
  "agentId": "agt_01HVZ...",
  "skill": "keyword_manager",
  "skillsUsed": ["keyword_manager", "research.keyword.list"],
  "status": "SUCCEEDED",
  "creditsUsed": 12,
  "llmCallsUsed": 4,
  "durationMs": 18420,
  "result": {
    "summary": "Found 12 PROBLEM_SOLUTION keywords missing landing pages.",
    "keywords": [
      {
        "id": "kw_***",
        "keyword": "track team capacity in real time",
        "kd": 22,
        "volume": 480
      },
      {
        "id": "kw_***",
        "keyword": "engineering project management for distributed teams",
        "kd": 31,
        "volume": 320
      }
    ]
  },
  "raw": "I started by listing every PROBLEM_SOLUTION keyword in the pricing pillar that didn't yet have a linked blog post or landing page. Of the 47 candidates, 12 had volume above 300 and KD under 35, which I shortlisted ...",
  "files": ["agent-workspace/shortlist.csv", "agent-workspace/ranking-notes.md"]
}

The universal response envelope

FieldAlways presentNotes
invocationIdyesinv_*** (CUID)
agentIdyesagt_*** (CUID) — the specific agent instance that ran this invocation
skillyesThe skill that was invoked
skillsUsedyesEvery skill the agent loaded during the run — primary + any sub-skills chained in
statusyesterminal: SUCCEEDED, FAILED, CANCELLED
resulton SUCCEEDEDStructured output, shape defined by the skill's outputSchema
rawon SUCCEEDED or FAILEDThe agent's raw text — its thinking, its narration, what it was about to do next. Higher fidelity than result.
filesalways (possibly empty)Array of file paths the agent wrote during the run. Fetch any of them with the Agent · files API.
erroron FAILEDStructured error

The raw and files fields let you reconstruct the agent's full reasoning when the structured result isn't enough. Treat result as the typed contract and raw+files as the audit trail.

If the agent exceeds the 60 s budget, the response is 202 Accepted (same as BACKGROUND), and you poll or subscribe.


GET /v1/agent/invocations/{id}

curl -H "Authorization: Bearer sk_live_***" \
  "https://api.citationbench.com/v1/agent/invocations/inv_01HVZ..."

Response

{
  "invocationId": "inv_01HVZ...",
  "agentId": "agt_01HVZ...",
  "skill": "bootstrap_brand",
  "skillsUsed": [
    "bootstrap_brand",
    "research.icp",
    "research.keyword",
    "research.competitor"
  ],
  "status": "WAITING_APPROVAL",
  "mode": "BACKGROUND",
  "workspace": "ws_acme",
  "depth": 0,
  "createdAt": "2026-05-24T08:00:00Z",
  "updatedAt": "2026-05-24T08:14:32Z",
  "startedAt": "2026-05-24T08:00:02Z",
  "creditsUsed": 87,
  "llmCallsUsed": 23,
  "stepsCompleted": ["crawl", "icp", "keywords", "competitors", "discussions"],
  "currentStep": {
    "name": "content_plan_publish",
    "status": "WAITING_APPROVAL",
    "approvalId": "appr_01HVZ...",
    "approvalUrl": "https://api.citationbench.com/v1/agent/approvals/appr_01HVZ..."
  },
  "children": [
    {
      "invocationId": "inv_01HVZ...A",
      "skill": "research.icp",
      "status": "SUCCEEDED"
    },
    {
      "invocationId": "inv_01HVZ...B",
      "skill": "research.keyword",
      "status": "SUCCEEDED"
    },
    {
      "invocationId": "inv_01HVZ...C",
      "skill": "research.competitor",
      "status": "SUCCEEDED"
    }
  ],
  "result": null,
  "files": [
    "agent-workspace/crawl-summary.md",
    "agent-workspace/icp-segments.json",
    "agent-workspace/keyword-shortlist.csv",
    "agent-workspace/competitor-matrix.csv",
    "agent-workspace/reddit-pain-points.md"
  ]
}

When the invocation reaches SUCCEEDED, result is populated with the structured output (its shape is defined by the skill's outputSchema).


GET /v1/agent/invocations/{id}/events (SSE)

A server-sent event stream. Useful for live progress (Claude Code, dashboards).

event: status.changed
data: { "from": "PENDING", "to": "RUNNING", "at": "2026-05-24T08:00:02Z" }

event: step.started
data: { "step": "crawl", "tool": "produce.crawl", "input": { "domain": "acme.com" } }

event: step.completed
data: { "step": "crawl", "durationMs": 6200, "result": { "pagesCrawled": 14 } }

event: step.started
data: { "step": "icp", "tool": "research.icp" }

event: step.awaiting_approval
data: { "step": "content_plan_publish", "approvalId": "appr_01HVZ...", "preview": { "weekCount": 12, "totalPosts": 48 } }

# ... (more events until status.changed → SUCCEEDED)

event: status.changed
data: { "from": "RUNNING", "to": "SUCCEEDED", "creditsUsed": 147 }

Event types: status.changed, step.started, step.completed, step.failed, step.awaiting_approval, step.approved, step.rejected, child.spawned, child.completed, llm.call, tool.call, cost.update.


POST /v1/agent/invocations/{id}/cancel

curl -X POST -H "Authorization: Bearer sk_live_***" \
  "https://api.citationbench.com/v1/agent/invocations/inv_01HVZ.../cancel" \
  -d '{ "reason": "Wrong workspace" }'

Response: 200 OK with the invocation in status: "CANCELLED". Any in-flight child invocations are also cancelled. Unspent reserved credits are refunded.


POST /v1/agent/invocations/{id}/continue

For multi-turn / chat skills (keyword_manager, link_swap_evaluator, custom conversational skills). Appends a message to the same session and resumes the agent.

curl -X POST -H "Authorization: Bearer sk_live_***" \
  "https://api.citationbench.com/v1/agent/invocations/inv_01HVZ.../continue" \
  -H "Content-Type: application/json" \
  -d '{
    "input": { "message": "Now drop everything with KD greater than 40." }
  }'

Response: a new invocation ID (each turn is its own invocation), with parentInvocationId set to the prior one and sessionId shared.


GET /v1/agent/skills

List all available skills and their schemas.

curl -H "Authorization: Bearer sk_live_***" \
  "https://api.citationbench.com/v1/agent/skills"

Response

{
  "skills": [
    {
      "slug": "bootstrap_brand",
      "name": "Bootstrap a brand",
      "description": "URL → full SEO+GEO operating plan in 20 min",
      "category": "research",
      "inputSchema": {
        "type": "object",
        "required": ["domain"],
        "properties": {
          "domain": {
            "type": "string",
            "description": "The brand's primary domain"
          },
          "depth": { "enum": ["fast", "thorough"], "default": "thorough" },
          "options": { "type": "object", "additionalProperties": true }
        }
      },
      "outputSchema": {
        "type": "object",
        "properties": {
          "workspaceId": { "type": "string" },
          "icp": { "type": "object" },
          "keywordUniverse": { "type": "object" },
          "competitors": { "type": "array" },
          "painPoints": { "type": "array" },
          "contentPlan": { "type": "object" },
          "landingPageBriefs": { "type": "array" }
        }
      },
      "defaultBudget": {
        "credits": 152,
        "llmCalls": 80,
        "durationSeconds": 1200
      },
      "supportsApproval": true,
      "supportsFiles": true
    },
    { "slug": "rank_monitor", "...": "..." },
    { "slug": "link_hunter", "...": "..." },
    { "slug": "citation_hunter", "...": "..." },
    { "slug": "content_factory", "...": "..." },
    { "slug": "refresh_stale", "...": "..." },
    { "slug": "keyword_manager", "...": "..." },
    { "slug": "link_swap_evaluator", "...": "..." }
  ],
  "customSkills": [
    {
      "slug": "custom:weekly-audit",
      "createdAt": "2026-05-12T...",
      "...": "..."
    }
  ]
}

MCP

Same surface, conversational.

> Use the bootstrap_brand skill to research acme.com. Pause before publishing anything.

Claude resolves the skill slug, fetches its schema, and calls agent.invoke with the right input shape. Progress streams as MCP notifications.

> What invocations are still waiting on me?

Claude calls agent.approval.list and renders the queue.


Errors

StatusCodeCause
400validation_errorinput failed the skill's schema; check the response details for the field
400unknown_skillskill is not in the registry or visible to your workspace
401unauthorizedMissing or invalid API key (or you're hitting an endpoint that doesn't allow demo mode)
402insufficient_creditsReserved cost exceeds remaining credits
403workspace_forbiddenKey cannot act on X-Workspace-Id
409idempotency_replaySame Idempotency-Key was used recently; returns the prior invocation
422budget_too_lowbudget.maxCredits below the skill's minimum
429rate_limitedPer-key rate exceeded
503skill_unavailableSkill depends on an external service that's down (e.g., DataForSEO)

Cost

Costs vary by skill. Built-in defaults:

SkillApprox creditsApprox duration
bootstrap_brand (thorough)15220 min
bootstrap_brand (fast)7810 min
rank_monitor (per workspace per run)1590 s
link_hunter (per cycle)505 min
citation_hunter (per cycle, 20 prompts × 4 LLMs)1608 min
content_factory (per article)504 min
refresh_stale (per article)303 min
keyword_manager (per turn)25 s

Use ?dryRun=true on the invoke call to get a precise estimate without running.


Use cases (string things together)

A. Bootstrap an agency client, then immediately publish 6 landing pages

# 1. Bootstrap
INV=$(curl -sf -X POST .../v1/agent/invoke -d '{
  "skill": "bootstrap_brand",
  "input": { "domain": "acme.com" }
}' | jq -r '.invocationId')

# 2. Wait
until [[ "$(curl -sf .../v1/agent/invocations/$INV | jq -r '.status')" =~ ^(SUCCEEDED|FAILED)$ ]]; do sleep 30; done

# 3. Spawn one content_factory invocation per landing-page brief
curl -sf .../v1/agent/invocations/$INV \
  | jq -r '.result.landingPageBriefs[] | @json' \
  | while read BRIEF; do
      curl -X POST .../v1/agent/invoke -d "{
        \"skill\": \"content_factory\",
        \"input\": { \"briefId\": $(echo $BRIEF | jq -r '.id') },
        \"mode\":  \"BACKGROUND\"
      }"
    done

B. One skill supervises another

A higher-level skill invokes a child skill and waits on its result.

const supervisor = await cb.agent.invoke({
  skill: "custom:editorial-supervisor",
  input: {
    // The supervisor's prompt instructs the agent to call content_factory
    // and then evaluate the output via produce.evaluate.
    keywordId: "kw_***",
  },
});

// Supervisor's tree shows the spawned content_factory child invocation:
const tree = await cb.agent.invocations.tree(supervisor.invocationId);

C. Pause-and-edit pattern for client review

const inv = await cb.agent.invoke({
  skill: "content_factory",
  input: { keywordIds: [...] },
  approval: { required: true, scope: "every_step" },
});

// Subscribe to events
const events = cb.agent.invocations.events(inv.invocationId);
for await (const ev of events) {
  if (ev.type === "step.awaiting_approval") {
    const decision = await ourPortal.askClient(ev.preview);
    if (decision.approve) {
      await cb.agent.approval.approve({ approvalId: ev.approvalId, edits: decision.edits });
    } else {
      await cb.agent.approval.reject({ approvalId: ev.approvalId, note: decision.note });
    }
  }
}

D. Run the same skill across every client workspace

curl -X POST .../v1/workspaces/bulk-action \
  -H "Authorization: Bearer sk_live_agency_***" \
  -d '{
    "action": "agent.invoke",
    "workspaces": "all",
    "config": {
      "skill": "rank_monitor",
      "input": { "scope": { "intent": ["PURCHASE"] }, "alertOn": { "drop": 5 } }
    }
  }'

Returns one parent invocation with one child per workspace.


On this page