HomeSDK Documentation

SDK Documentation

Canonical reference for the DashClaw SDK (v2.11.1). Node.js and Python parity across all core governance features.

Quick Start

1

Install

npm install dashclaw
2

Initialize

import { DashClaw } from 'dashclaw';

const claw = new DashClaw({
  baseUrl: process.env.DASHCLAW_BASE_URL,
  apiKey: process.env.DASHCLAW_API_KEY,
  agentId: 'my-agent'
});
3

Governance Loop

// 1. Ask permission — abort on hard block
const decision = await claw.guard({
  action_type: 'deploy',
  risk_score: 85,
  declared_goal: 'Update auth service to v2.1.1'
});

if (decision.decision === 'block') {
  throw new Error(`Blocked: ${decision.reason || decision.reasons?.join(', ')}`);
}

// 2. Log intent. The server re-evaluates policy here and is the
//    authoritative source for HITL gating.
const { action, action_id } = await claw.createAction({
  action_type: 'deploy',
  declared_goal: 'Update auth service to v2.1.1'
});

// 3. If the server flagged this, wait for a human operator.
//    Pass createAction's action_id — NOT decision.action_id.
if (action?.status === 'pending_approval') {
  await claw.waitForApproval(action_id);
}

try {
  // 4. Log evidence
  await claw.recordAssumption({
    action_id,
    assumption: 'Tests passed'
  });

  // ... deploy ...

  // 5. Record outcome
  await claw.updateOutcome(action_id, { status: 'completed' });
} catch (err) {
  await claw.updateOutcome(action_id, { status: 'failed', error_message: err.message });
}

MCP Server

@dashclaw/mcp-server exposes DashClaw governance over Model Context Protocol. Any MCP-compatible client gets 8 governance tools and 4 read-only resources.

Tools (8)

ToolDescriptionKey Inputs
dashclaw_guardEvaluate policies before risky actionsaction_type, declared_goal, risk_score
dashclaw_recordLog action to audit trailaction_type, declared_goal, status
dashclaw_invokeExecute governed capabilitycapability_id, declared_goal, payload
dashclaw_capabilities_listDiscover available APIscategory, risk_level, search
dashclaw_policies_listList active policiesagent_id
dashclaw_wait_for_approvalWait for human decisionaction_id, timeout_seconds
dashclaw_session_startRegister agent sessionagent_id, workspace
dashclaw_session_endClose sessionsession_id, status, summary

Resources (4)

URIDescription
dashclaw://policiesActive policy set
dashclaw://capabilitiesAvailable capabilities and health
dashclaw://agent/{agent_id}/historyRecent action history (last 50)
dashclaw://statusInstance health + operational metrics

Configuration

Config resolution: CLI args > env vars > defaults. Three config values: url (DASHCLAW_URL, default localhost:3000), apiKey (DASHCLAW_API_KEY), agentId (DASHCLAW_AGENT_ID).

stdio — Claude Code / Desktop (claude_desktop_config.json)
{
  "mcpServers": {
    "dashclaw": {
      "command": "npx",
      "args": ["@dashclaw/mcp-server"],
      "env": {
        "DASHCLAW_URL": "https://your-instance.vercel.app",
        "DASHCLAW_API_KEY": "oc_live_..."
      }
    }
  }
}
Streamable HTTP — Managed Agents (Python)
mcp_servers=[{
    "type": "url",
    "url": "https://your-instance.vercel.app/api/mcp",
    "headers": {"x-api-key": "oc_live_..."},
    "name": "dashclaw"
}]

CLI & Doctor

@dashclaw/cli handles terminal approvals and self-host diagnostics. npm run doctor runs the same engine locally with filesystem-level fix powers.

dashclaw doctor

Diagnoses database, configuration, auth, deployment, SDK reachability, governance staleness, and livingcode shape drift — auto-fixing safe issues. Invokes GET /api/doctor and POST /api/doctor/fix. For operators, npm run doctor on the host adds .env writes, migrations, and default-policy seeding (backs up .env before any write).

dashclaw doctor
npm install -g @dashclaw/cli

dashclaw doctor                          # rich terminal output, auto-fix safe issues
dashclaw doctor --json                   # CI / scripts
dashclaw doctor --no-fix                 # diagnose only
dashclaw doctor --category database,config

# Config resolution: env vars → ~/.dashclaw/config.json (600) → interactive prompt
dashclaw logout                          # remove saved config

# Self-host operator (filesystem-level fixes)
npm run doctor

OpenClaw Plugin

@dashclaw/openclaw-plugin wires governance into the OpenClaw agent framework. Intercepts PreToolUse / PostToolUse lifecycle hooks, calls guard / record / wait-for-approval automatically, and ships a HOOK.md pack the openclaw CLI installs. Tool classification vocabulary aligns with DashClaw guard action types.

Governance Skill (Claude)

@dashclaw/governance is an Anthropic Claude skill that teaches governed agents how to use the MCP tools correctly — risk thresholds, decision handling, recording rules, session lifecycle. Pairs with @dashclaw/mcp-server for Managed Agents. Download the zip from your instance at /downloads/dashclaw-governance.zip.

Constructor

const claw = new DashClaw({ baseUrl, apiKey, agentId });
ParameterTypeRequiredDescription
baseUrl / base_urlstringYesDashboard URL
apiKey / api_keystringYesAPI Key
agentId / agent_idstringYesUnique Agent ID

Behavior Guard

claw.guard(context)

Evaluate guard policies for a proposed action. Call this before risky operations. The guard response includes a `learning` field with historical performance context when available (recent scores, drift status, learned patterns, feedback summary).

ParameterTypeRequiredDescription
action_typestringYesProposed action type
risk_scorenumberNo0-100

Returns: Promise<{ decision: string, reasons: string[], risk_score: number, agent_risk_score: number | null }>

const result = await claw.guard({ action_type: 'deploy', risk_score: 85 });

Action Recording

claw.createAction(action) / claw.create_action(**kwargs)

Create a governance action record. The server re-evaluates policy at this point, so this call is the authoritative source for HITL gating: if policy requires human review, the response is HTTP 202 with action.status='pending_approval'. Always check action.status before assuming the action is clear to execute.

Returns: Promise<{ action: { action_id, status, ... }, action_id, decision, security }>

const { action, action_id } = await claw.createAction({ action_type: 'deploy' });
if (action?.status === 'pending_approval') {
  // gate execution on waitForApproval — see the method below
}

claw.waitForApproval(actionId, { timeout?, interval? }) / claw.wait_for_approval(action_id, timeout=300, interval=5)

Wait for a human operator to approve or deny an action. Opens an SSE stream on /api/stream and falls back to polling /api/actions/:id every 5 seconds. Resolves when action.approved_by is set; throws ApprovalDeniedError when the operator denies; throws on timeout. IMPORTANT: pass the action_id returned by createAction() — NOT the action_id returned by guard(). They refer to different database tables and waiting on a guard decision ID will never resolve. Approvals can be resolved from the dashboard (/approvals), the CLI (dashclaw approve <id>), the mobile PWA (/approve), or — if the instance has Telegram configured (TELEGRAM_BOT_TOKEN) — via an inline Approve/Reject button pushed to the admin Telegram chat. All four surfaces call the same /api/approvals/:id endpoint, so waitForApproval unblocks the agent within ~1 second regardless of which surface was used.

// Correct — wait on createAction's action_id
const { action, action_id } = await claw.createAction({ action_type: 'deploy' });
if (action?.status === 'pending_approval') {
  await claw.waitForApproval(action_id, { timeout: 600_000 });
}

claw.updateOutcome(id, outcome) / claw.update_outcome(id, **kwargs)

Log final results. Accepts status, output_summary, error_message, duration_ms, tokens_in, tokens_out, model, cost_estimate. When tokens + model are supplied without cost_estimate, the server derives cost from the pricing table.

await claw.updateOutcome(action_id, {
  status: 'completed',
  tokens_in: result.usage.input_tokens,
  tokens_out: result.usage.output_tokens,
  model: result.model,
});

claw.recordAssumption(asm) / claw.record_assumption(asm)

Track agent beliefs.

await claw.recordAssumption({ action_id, assumption: '...' });

Signals

claw.getSignals() / claw.get_signals()

Get current risk signals across all agents.

Returns: Promise<{ signals: Object[] }>

const { signals } = await claw.getSignals();

Agent Lifecycle

claw.heartbeat(status, metadata) / claw.heartbeat(status=..., metadata=...)

Report agent presence and health to the control plane. Call periodically to indicate the agent is alive.

ParameterTypeRequiredDescription
statusstringNoAgent status — 'online', 'busy', 'idle'. Defaults to 'online'
metadataobjectNoArbitrary metadata to include with the heartbeat
await claw.heartbeat('online', { cycle: 42, uptime_ms: 360000 });

claw.reportConnections(connections) / claw.report_connections(connections)

Report active provider connections and their status. Appears in the agent's Fleet profile.

ParameterTypeRequiredDescription
connectionsArray<Object>YesList of { name, type, status } connection objects
await claw.reportConnections([
  { name: 'OpenAI', type: 'llm', status: 'connected' },
  { name: 'Postgres', type: 'database', status: 'connected' },
]);

Loops & Assumptions

claw.registerOpenLoop(actionId, type, desc) / claw.register_open_loop(...)

Register an unresolved dependency for a decision. Open loops track work that must be completed before the decision is fully resolved.

ParameterTypeRequiredDescription
action_idstringYesAssociated action
loop_typestringYesThe category of the loop
descriptionstringYesWhat needs to be resolved
await claw.registerOpenLoop(action_id, 'validation', 'Waiting for PR review');

claw.resolveOpenLoop(loopId, status, res) / claw.resolve_open_loop(...)

Resolve a pending loop.

await claw.resolveOpenLoop(loop_id, 'completed', 'Approved');

claw.recordAssumption(asm) / claw.record_assumption(asm)

Record what the agent believed to be true when making a decision.

await claw.recordAssumption({ action_id, assumption: 'User is authenticated' });

Learning Analytics

claw.getLearningVelocity() / claw.get_learning_velocity()

Compute learning velocity (rate of score improvement) for agents.

Returns: Promise<{ velocity: Array<Object> }>

const { velocity } = await claw.getLearningVelocity();

claw.getLearningCurves() / claw.get_learning_curves()

Compute learning curves per action type to measure efficiency gains.

const curves = await claw.getLearningCurves();

claw.getLessons({ actionType, limit }) / claw.get_lessons(action_type=..., limit=...)

Fetch consolidated lessons from scored outcomes — what DashClaw has learned about this agent's performance patterns.

ParameterTypeRequiredDescription
actionTypestringNoFilter by action type
limitnumberNoMax lessons to return (default 10)

Returns: Promise<{ lessons: Object[], drift_warnings: Object[], agent_id: string }>

const { lessons, drift_warnings } = await claw.getLessons({ actionType: 'deploy' });
lessons.forEach(l => console.log(l.guidance));

Prompt Management

claw.renderPrompt() / claw.render_prompt()

Fetch rendered prompt from DashClaw.

const { rendered } = await claw.renderPrompt({
  template_id: 'marketing',
  variables: { company: 'Apple' }
});

Evaluation Framework

claw.createScorer(name, type, config) / claw.create_scorer(...)

Create a reusable scorer definition for automated evaluation.

ParameterTypeRequiredDescription
namestringYesScorer name
scorer_typestringYesType (llm_judge, regex, range)
configobjectNoScorer configuration
await claw.createScorer('toxicity', 'regex', { pattern: 'bad-word' });

Scoring Profiles

claw.createScoringProfile(config) / claw.create_scoring_profile(...)

Define weighted quality scoring profiles across multiple scorers.

await claw.createScoringProfile({ 
  name: 'prod-quality', 
  dimensions: [{ scorer: 'toxicity', weight: 0.5 }] 
});

Agent Messaging

claw.sendMessage(params) / claw.send_message(**kwargs)

Send a point-to-point message or broadcast to all agents in the organization.

ParameterTypeRequiredDescription
tostringNoTarget agent ID (omit for broadcast)
bodystringYesMessage content
typestringNoaction|info|lesson|question
urgentbooleanNoMark as high priority
await claw.sendMessage({
  to: 'scout-agent-01',
  body: 'I have finished indexing the repository. You can start the analysis.',
  type: 'status'
});

claw.getInbox(options?) / claw.get_inbox(**kwargs)

Retrieve messages from the agent inbox with optional filtering.

ParameterTypeRequiredDescription
typestringNoFilter by message type
unreadbooleanNoOnly return unread messages
limitnumberNoMax messages to return

Returns: Promise<{ messages, total, unread_count }>

const { messages } = await claw.getInbox({ unread: true, limit: 10 });

Session Handoffs

claw.createHandoff(handoff) / claw.create_handoff(**kwargs)

Create a session handoff document to persist state between agent sessions or transfer context to another agent.

await claw.createHandoff({
  summary: 'Completed initial data collection from Jira.',
  key_decisions: ['Prioritize high-severity bugs', 'Ignore closed tickets'],
  open_tasks: ['Run security scan on src/', 'Draft fix for #123'],
  next_priorities: ['Security audit']
});

claw.getLatestHandoff() / claw.get_latest_handoff()

Retrieve the most recent handoff for the current agent.

Returns: Promise<Object|null>

const handoff = await claw.getLatestHandoff();

Security Scanning

claw.scanPromptInjection(text) / claw.scan_prompt_injection(text)

Scan untrusted input for potential prompt injection or jailbreak attempts.

ParameterTypeRequiredDescription
textstringYesUntrusted input to scan

Returns: Promise<{ clean: boolean, risk_level: string, recommendation: string }>

const result = await claw.scanPromptInjection(userInput);
if (!result.clean) {
  console.warn('Injection risk:', result.risk_level);
}

User Feedback

claw.submitFeedback(params) / claw.submit_feedback(**kwargs)

Submit feedback for a specific agent action. Used for human evaluation of agent performance.

ParameterTypeRequiredDescription
action_idstringYesTarget action ID
ratingnumberYes1-5 star rating
commentstringNoTextual feedback
categorystringNoGrouping tag
await claw.submitFeedback({
  action_id: 'act_4b2s8...',
  rating: 4,
  comment: 'Action was safe and effective but took longer than expected.',
  category: 'performance_review'
});

Context Threads

claw.createThread(options) / claw.create_thread(**kwargs)

Create a new context thread to track a multi-step reasoning chain or investigation.

ParameterTypeRequiredDescription
namestringYesThread name
summarystringNoInitial thread summary

Returns: Promise<{ thread, thread_id }>

const { thread } = await claw.createThread({ name: 'Deploy analysis', summary: 'Evaluating safety' });

claw.addThreadEntry(threadId, content, entryType) / claw.add_thread_entry(...)

Append an observation, conclusion, or decision to an existing context thread.

ParameterTypeRequiredDescription
threadIdstringYesThread ID to append to
contentstringYesEntry content
entryTypestringYes'observation' | 'conclusion' | 'decision'
await claw.addThreadEntry('ct_abc123', 'Staging checks passed', 'observation');

claw.closeThread(threadId, summary?) / claw.close_thread(thread_id, summary=None)

Close a context thread, optionally providing a final summary.

ParameterTypeRequiredDescription
threadIdstringYesThread ID to close
summarystringNoFinal summary of the thread
await claw.closeThread('ct_abc123', 'Deploy approved after staging check');

Bulk Sync

claw.syncState(state) / claw.sync_state(**kwargs)

Bulk-sync agent state including decisions, lessons, goals, context, relationships, memory, and preferences in a single call.

ParameterTypeRequiredDescription
stateobjectYesState object with keys: decisions, lessons, goals, context, relationships, memory, preferences
await claw.syncState({ decisions: [...], lessons: [...], goals: [...] });

Agent Identity

Enroll agents via public-key pairing and manage approved identities. Pairing requests are created by agents; approval is an admin action. Once approved, the agent's public key is registered as a trusted identity for signature verification.

POST /api/pairings

Create an agent pairing request. The agent submits its public key and waits for operator approval.

ParameterTypeRequiredDescription
public_keystringYesPEM-encoded RSA public key
algorithmstringNoKey algorithm. Default: RSASSA-PKCS1-v1_5
agent_namestringNoHuman-readable label for the agent

Returns: { pairing: { id, status, agent_name, created_at } }

Create pairing request
// Node SDK (v1 legacy)
import { DashClaw } from 'dashclaw/legacy';
const claw = new DashClaw({ baseUrl, apiKey, agentId });

const { pairing } = await claw.createPairing(publicKeyPem, 'RSASSA-PKCS1-v1_5', 'my-agent');
console.log(pairing.id); // pair_...

GET /api/pairings

List all pairing requests for the organization. Admin API key required.

Returns: { pairings: Array<{ id, status, agent_name, created_at, approved_at }> }

List pairings (admin)
const res = await fetch('/api/pairings', {
  headers: { 'x-api-key': adminApiKey }
});
const { pairings } = await res.json();

GET /api/pairings/:id

Get a specific pairing request by ID. Used by agents to poll for approval status.

Returns: { pairing: { id, status, agent_name, created_at, approved_at } }

Poll pairing status
// Node SDK (v1 legacy)
const status = await claw.getPairing(pairingId);
console.log(status.pairing.status); // pending | approved | expired

POST /api/pairings/:id/approve

Approve a pending pairing request. Admin API key required. On approval, the agent's public key is registered as a trusted identity.

Returns: { pairing: { id, status, approved_at } }

Approve pairing (admin)
const res = await fetch(`/api/pairings/${pairingId}/approve`, {
  method: 'POST',
  headers: { 'x-api-key': adminApiKey }
});

POST /api/identities

Directly register an agent's public key as a trusted identity. Admin API key required. Bypasses the pairing flow.

ParameterTypeRequiredDescription
agent_idstringYesUnique agent identifier
public_keystringYesPEM-encoded RSA public key
algorithmstringNoKey algorithm. Default: RSASSA-PKCS1-v1_5

Returns: { identity: { agent_id, algorithm, created_at } }

Register identity (admin)
// Node SDK (v1 legacy)
await claw.registerIdentity('agent-007', publicKeyPem, 'RSASSA-PKCS1-v1_5');

GET /api/identities

List all registered agent identities for the organization. Admin API key required.

Returns: { identities: Array<{ agent_id, algorithm, created_at }> }

List identities (admin)
// Node SDK (v1 legacy)
const { identities } = await claw.getIdentities();

DELETE /api/identities/:agentId

Revoke a registered agent identity. Admin API key required. The agent's public key is removed and signature verification will fail for future actions.

Returns: { success: true }

Revoke identity (admin)
const res = await fetch(`/api/identities/${agentId}`, {
  method: 'DELETE',
  headers: { 'x-api-key': adminApiKey }
});

Execution Studio (HTTP API)

Governance packaging: workflow templates, model strategies, knowledge collections, a capability registry, and a read-only execution graph on actions. Every surface here has a canonical SDK wrapper method in the v2 Node SDK (see sdk/dashclaw.js, 80 methods total). The HTTP examples below are shown first because they're language-agnostic; the equivalent SDK calls (claw.listWorkflowTemplates, claw.execution.capabilities.invoke, etc.) are in sdk/README.md → Execution Studio. Full OpenAPI definitions are at docs/openapi/critical-stable.openapi.json.

Execution Graph

GET /api/actions/:actionId/graph

Read-only execution graph (nodes + edges) for any action. Reuses the existing trace data plus correlated assumptions and open loops — zero schema change. Powers the Graph tab on decision replay.

Returns: { rootActionId, nodes: Array<{ id, type, status, riskScore, ... }>, edges: Array<{ source, target, type, label }> }

Fetch graph
const res = await fetch(`${baseUrl}/api/actions/${actionId}/graph`, {
  headers: { 'x-api-key': apiKey }
});
const { rootActionId, nodes, edges } = await res.json();
// node ids: action:<id>, assumption:<id>, loop:<id>
// edge types: parent_child | related | assumption_of | loop_from

Workflow Templates

Package a repeatable operational pattern as a reusable, versioned asset linking policies, prompts, knowledge, capabilities, and a model strategy.

GET /api/workflows/templates

List all workflow templates for the current org. Supports ?status=draft|active|archived, ?limit, ?offset.

List templates
const { templates } = await fetch(`${baseUrl}/api/workflows/templates`, {
  headers: { 'x-api-key': apiKey }
}).then(r => r.json());

POST /api/workflows/templates

Create a workflow template. Slug auto-generated from name if omitted. Starts as v1, status=draft. Body fields: name (required), description, objective, steps, linked_prompt_template_ids, linked_policy_ids, linked_knowledge_collection_ids, linked_capability_ids, linked_capability_tags, model_strategy_id, status.

Create template
await fetch(`${baseUrl}/api/workflows/templates`, {
  method: 'POST',
  headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Release Hotfix',
    description: 'Ship urgent production patches safely',
    objective: 'Deploy with full policy + approval coverage',
    linked_policy_ids: ['pol_prod_deploy'],
    linked_capability_tags: ['deploy'],
    model_strategy_id: 'mst_balanced_default'
  })
});

GET | PATCH /api/workflows/templates/:templateId

Fetch or partially update a template. PATCH bumps version by 1 when the steps array changes; all linked arrays and metadata can be updated in the same call.

Update steps (bumps version)
await fetch(`${baseUrl}/api/workflows/templates/${templateId}`, {
  method: 'PATCH',
  headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    steps: [{ id: 'plan' }, { id: 'test' }, { id: 'deploy' }]
  })
});

POST /api/workflows/templates/:templateId/duplicate

Clone a template as a new draft (version resets to 1, status='draft'). Accepts optional name and slug overrides in the body.

Duplicate
const { template } = await fetch(
  `${baseUrl}/api/workflows/templates/${templateId}/duplicate`,
  { method: 'POST', headers: { 'x-api-key': apiKey } }
).then(r => r.json());

POST /api/workflows/templates/:templateId/launch

Launch a template. Creates a new row in action_records with trigger='workflow:<templateId>' and reasoning='WORKFLOW_LAUNCH_META=<json>' carrying the full template context. If the template links a model_strategy_id, the resolved config is fetched and snapshotted onto the launched action and the template. No schema columns were added to action_records — Phase 1 piggybacks on existing trace primitives.

Returns: { launch: { action_id, template_id, template_version, launched_at, resolved_strategy } }

Launch and link to replay
const { launch } = await fetch(
  `${baseUrl}/api/workflows/templates/${templateId}/launch`,
  {
    method: 'POST',
    headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
    body: JSON.stringify({ agent_id: 'deploy-bot' })
  }
).then(r => r.json());

// The launched action is immediately traceable in decision replay
console.log(`${baseUrl}/decisions/${launch.action_id}`);

GET /api/workflows/templates/:templateId/runs

List past workflow executions for a template. Each run is a parent action_record with step counts from workflow_step_results. Supports status, agent_id, limit, and offset query params.

Returns: { template_id, runs: [{ run_action_id, template_id, status, agent_id, declared_goal, duration_ms, started_at, finished_at, step_count, steps_completed, steps_failed }], total }

List recent runs
const runs = await fetch(
  `${baseUrl}/api/workflows/templates/${templateId}/runs?limit=10`,
  { headers: { 'x-api-key': apiKey } }
).then(r => r.json());

runs.runs.forEach(r =>
  console.log(`${r.status} — ${r.steps_completed}/${r.step_count} steps — ${r.duration_ms}ms`)
);

GET /api/workflows/templates/:templateId/runs/:runActionId

Fetch full run detail including all step results with complete input/output JSON. Powers the run detail page. Each step includes the resolved input after variable interpolation and the full output (no truncation).

Returns: { run_action_id, template_id, template_name, status, agent_id, declared_goal, duration_ms, started_at, finished_at, error_message, steps: [{ step_id, step_index, step_type, step_name, status, input, output, error_message, retry_count, duration_ms }] }

Inspect a failed run
const run = await fetch(
  `${baseUrl}/api/workflows/templates/${templateId}/runs/${runActionId}`,
  { headers: { 'x-api-key': apiKey } }
).then(r => r.json());

const failed = run.steps.filter(s => s.status === 'failed');
failed.forEach(s =>
  console.log(`Step ${s.step_name} failed: ${s.error_message}`)
);

Model Strategies

Reusable provider/model strategy records (primary + fallback chain, cost/latency sensitivity, budget cap). Linked from workflow templates and snapshotted at launch.

GET | POST /api/model-strategies

List all strategies or create a new one. Config is validated server-side: primary.provider and primary.model are required; costSensitivity must be one of low | balanced | high-quality; latencySensitivity must be low | medium | high; maxBudgetUsd must be a number; maxRetries must be an integer; fallback, allowedProviders, and disallowedProviders must be arrays if provided.

Create strategy
await fetch(`${baseUrl}/api/model-strategies`, {
  method: 'POST',
  headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Balanced Default',
    description: 'GPT-4.1 primary, Claude Sonnet 4 fallback',
    config: {
      primary: { provider: 'openai', model: 'gpt-4.1' },
      fallback: [{ provider: 'anthropic', model: 'claude-sonnet-4' }],
      costSensitivity: 'balanced',
      latencySensitivity: 'medium',
      maxBudgetUsd: 0.5,
      maxRetries: 2,
      allowedProviders: ['openai', 'anthropic']
    }
  })
});

GET | PATCH | DELETE /api/model-strategies/:strategyId

Fetch, update, or delete a strategy. PATCH merges config patches over the existing config (primary fields preserved unless overridden). DELETE nulls out the soft reference on any linked workflow_templates rather than orphaning them.

Patch budget only
await fetch(`${baseUrl}/api/model-strategies/${strategyId}`, {
  method: 'PATCH',
  headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
  body: JSON.stringify({ config: { maxBudgetUsd: 1.0 } })
});

POST /api/model-strategies/:strategyId/complete

Execute a chat completion using this strategy. Resolves BYOK provider credentials from org settings, walks the fallback chain (primary provider first, then each fallback), enforces maxBudgetUsd, and returns a normalized response. Supports task_mode to override primary with the corresponding taskModes entry. Providers supported: openai, anthropic, groq, together, perplexity. Returns 502 with provider_errors array when all providers fail.

ParameterTypeRequiredDescription
messagesArray<{ role, content }>YesChat messages (system, user, assistant)
max_tokensnumberNoMax output tokens (default 1024)
temperaturenumberNoSampling temperature (default 0.7)
task_modestringNoOverride primary with taskModes[mode] if defined in strategy config

Returns: { content, provider, model, usage: { input_tokens, output_tokens }, cost_usd, fallback_used, attempts, strategy_id, strategy_name }

Execute completion with fallback
const result = await claw.completeWithStrategy(strategyId, [
  { role: 'user', content: 'Summarize the deploy plan' }
], { max_tokens: 512, task_mode: 'reasoning' });

console.log(result.content);       // LLM response
console.log(result.provider);      // which provider handled it
console.log(result.cost_usd);      // estimated cost
console.log(result.fallback_used); // true if primary failed

Knowledge Collections

Lightweight metadata layer for knowledge sources that workflows and agents can bind to. No embedding or retrieval in Phase 1 — metadata + tags only.

GET | POST /api/knowledge/collections

List collections (filter by ?source_type) or create a new one. source_type must be one of files | urls | external | notes. New collections start with ingestion_status='empty' and doc_count=0.

Create collection
await fetch(`${baseUrl}/api/knowledge/collections`, {
  method: 'POST',
  headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Runbook Library',
    description: 'Incident response runbooks',
    source_type: 'files',
    tags: ['ops', 'oncall']
  })
});

GET | PATCH /api/knowledge/collections/:collectionId

Fetch or update a collection's metadata (name, description, source_type, tags, ingestion_status).

Fetch
const { collection } = await fetch(
  `${baseUrl}/api/knowledge/collections/${collectionId}`,
  { headers: { 'x-api-key': apiKey } }
).then(r => r.json());

GET | POST /api/knowledge/collections/:collectionId/items

List or add items in a collection. Adding an item increments the parent collection's doc_count atomically and transitions ingestion_status from 'empty' to 'pending' on the first item. Items carry source_uri (required), title, mime_type, status, and a metadata object.

Add an item
await fetch(
  `${baseUrl}/api/knowledge/collections/${collectionId}/items`,
  {
    method: 'POST',
    headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      source_uri: 'https://docs.example.com/runbook.md',
      title: 'Deploy runbook',
      mime_type: 'text/markdown'
    })
  }
);

POST /api/knowledge/collections/:collectionId/sync

Caller-invoked ingestion: fetches source_uri content for each pending item, chunks text (~500 tokens with overlap), generates embeddings via BYOK OpenAI key (text-embedding-3-small, 1536 dims), and stores in the knowledge_chunks table (pgvector). Updates item status (pending → indexed/failed) and collection ingestion_status. Bounded to 50 items per call — designed for Vercel free tier (no cron required).

Returns: { sync: { ingested, failed, chunks_created, errors } }

Sync a collection
// SDK
const { sync } = await claw.syncKnowledgeCollection(collectionId);
console.log(sync.ingested, sync.chunks_created);

POST /api/knowledge/collections/:collectionId/search

Semantic search over chunked + embedded content. Embeds the query via BYOK OpenAI key, then uses pgvector cosine distance to find the most relevant chunks. Returns top-k results with similarity scores, chunk content, and source item metadata.

ParameterTypeRequiredDescription
querystringYesNatural language search query
limitnumberNoMax results (default 5, max 20)

Returns: { query, collection_id, results: Array<{ chunk_id, item_id, content, score, position, token_count, title, source_uri }>, count }

Search a collection
const { results } = await claw.searchKnowledgeCollection(
  collectionId,
  'How do I roll back a deploy?',
  { limit: 5 }
);
results.forEach(r => console.log(`${(r.score * 100).toFixed(1)}%: ${r.content.slice(0, 80)}`));

Capability Registry

Governed registry of callable capabilities with risk, approval, health, and (future) pricing metadata. Workflow templates can reference capabilities by id or by tag.

GET | POST /api/capabilities

Search or register a capability. GET supports combinable filters: ?category, ?risk_level (low|medium|high|critical), ?search (ILIKE on name/description/tags). source_type must be one of internal_sdk | http_api | webhook | human_approval | external_marketplace. (org_id, slug) is unique — POST returns 409 on duplicate slug.

Register a capability
await fetch(`${baseUrl}/api/capabilities`, {
  method: 'POST',
  headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Send Slack Message',
    description: 'Posts to a configured Slack channel',
    category: 'messaging',
    source_type: 'http_api',
    auth_type: 'oauth',
    risk_level: 'medium',
    requires_approval: false,
    tags: ['notify', 'slack'],
    health_status: 'healthy',
    docs_url: 'https://docs.example.com/slack'
  })
});

GET | PATCH /api/capabilities/:capabilityId

Fetch or update a capability. PATCH validates risk_level and source_type enums on change.

Mark degraded
await fetch(`${baseUrl}/api/capabilities/${capabilityId}`, {
  method: 'PATCH',
  headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
  body: JSON.stringify({ health_status: 'degraded' })
});

Capability Runtime

Governed capability invocation with retry policies, circuit breaker, and health tracking. Capabilities with retry_policy retry transient failures automatically. Capabilities with circuit_breaker auto-block after consecutive failures (reset via test route).

POST /api/capabilities/:capabilityId/invoke

Execute a governed capability invocation. Evaluates guard policies, scans for sensitive data, enforces quota, runs the HTTP call with optional retry, and records a full action audit trail. Returns retry_metadata when retry_policy is configured. Returns 503 circuit_breaker_open when the circuit breaker is tripped.

Invoke with payload
const res = await fetch(`${baseUrl}/api/capabilities/${capabilityId}/invoke`, {
  method: 'POST',
  headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
  body: JSON.stringify({ query: 'What is x402?' })
});
const data = await res.json();
// data.success, data.action_id, data.result, data.elapsed_ms, data.governed
// data.retry_metadata (when retry_policy configured): { total_attempts, retried, attempts }

POST /api/capabilities/:capabilityId/test

Run a non-production validation call. Bypasses guard policies and circuit breaker. Updates capability health_status and certification_status based on the result. Use this to certify a capability or reset an open circuit breaker.

Test a capability
const res = await fetch(`${baseUrl}/api/capabilities/${capabilityId}/test`, {
  method: 'POST',
  headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
  body: JSON.stringify({ query: 'test input' })
});
const data = await res.json();
// data.tested, data.health_status, data.certification_status

GET /api/capabilities/:capabilityId/health

Fetch derived health summary including success rates (1d/7d), p95 latency, certification status, recent errors, and stale check. Computed from action_records over the past 7 days.

Check capability health
const res = await fetch(`${baseUrl}/api/capabilities/${capabilityId}/health`, {
  headers: { 'x-api-key': apiKey }
});
const health = await res.json();
// health.status (healthy|degraded|failing|untested)
// health.certification_status (certified|stale|failed|uncertified)
// health.success_rate_1d, health.success_rate_7d, health.p95_latency_ms

GET /api/capabilities/:capabilityId/history

Fetch invocation and test event history for a capability. Filter by action_type (capability_invoke, capability_test) and status (completed, failed, running, pending_approval). Supports limit and offset pagination.

Fetch recent failures
const res = await fetch(`${baseUrl}/api/capabilities/${capabilityId}/history?status=failed&limit=10`, {
  headers: { 'x-api-key': apiKey }
});
const history = await res.json();
// history.events[].action_id, action_type, status, error_message, duration_ms

Analytics

GET /api/analytics

Fetch aggregated governance analytics for the organization over a rolling window. Includes action counts, guard decision totals, signal summaries, and assumption stats. Supports ?days (1–365, default 30).

ParameterTypeRequiredDescription
daysnumberNoRolling window in days (1–365). Defaults to 30.

Returns: { actions_total, actions_by_status, guard_decisions_total, guard_decisions_by_outcome, signals_total, assumptions_total }

Fetch 7-day analytics
const res = await fetch(`${baseUrl}/api/analytics?days=7`, {
  headers: { 'x-api-key': apiKey }
});
const data = await res.json();
// data.actions_total, data.guard_decisions_total, data.signals_total

Guard Decisions

GET /api/guard/decisions

List guard evaluation records for the organization. Returns paginated decisions with matched policies and declared goal context. Supports filtering by ?decision (allow|block|flag), ?agent_id, ?limit (max 200), and ?offset.

ParameterTypeRequiredDescription
decisionstringNoFilter by outcome: allow | block | flag
agent_idstringNoFilter to a specific agent
limitnumberNoPage size (max 200, default 50)
offsetnumberNoPagination offset (default 0)

Returns: { decisions: Array<{ id, agent_id, action_type, decision, matched_policies, declared_goal, agent_name, created_at }>, total, stats }

List blocked decisions
const res = await fetch(`${baseUrl}/api/guard/decisions?decision=block&limit=25`, {
  headers: { 'x-api-key': apiKey }
});
const { decisions, total, stats } = await res.json();
// decisions[].decision, decisions[].matched_policies, decisions[].declared_goal

Agent Profile

GET /api/agents/:agentId/profile

Fetch the full governance profile for a specific agent. Includes identity, presence (heartbeat state), trust posture, computed risk signals, and assumptions summary. Returns 404 if the agent has not been seen by the instance.

ParameterTypeRequiredDescription
agentIdstringYesThe agent identifier (path parameter)

Returns: { agent: { agent_id, agent_name, action_count, last_active, presence: { status, last_heartbeat_at, current_task_id } }, trust, signals, assumptions_summary }

Fetch agent profile
const res = await fetch(`${baseUrl}/api/agents/my-agent/profile`, {
  headers: { 'x-api-key': apiKey }
});
const { agent, trust, signals, assumptions_summary } = await res.json();
// agent.presence.status, trust.risk_score, signals, assumptions_summary

Error Handling

Error shape
{ message: "Validation failed", status: 400 }