Canonical reference for the DashClaw SDK (Node v4.7.11 / Python v4.7.11). Node.js and Python parity across all core governance features.
npm install dashclaw
import { DashClaw } from 'dashclaw';
const claw = new DashClaw({
baseUrl: process.env.DASHCLAW_BASE_URL,
apiKey: process.env.DASHCLAW_API_KEY,
agentId: 'my-agent'
});// 1. Ask permission — abort on hard block
const decision = await claw.guard({
action_type: 'deploy',
risk_score: 85,
declared_goal: 'Update the auth service'
});
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 the auth service'
});
// 3. If the server flagged this, wait for a human operator.
// Pass createAction's action_id — NOT the guard's decision_id (a.k.a. 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 });
}@dashclaw/mcp-server exposes DashClaw governance over Model Context Protocol. Any MCP-compatible client gets 29 governance tools across 10 groups (core governance, optimal files, session continuity, credential hygiene, skill safety, open loops, learning + retrospection, agent inbox, behavior learning, governance posture) plus 6 read-only resources.
| Tool | Description | Key Inputs |
|---|---|---|
Core governance dashclaw_guard | Evaluate policies before risky actions | action_type, declared_goal, risk_score |
| dashclaw_record | Log action to audit trail | action_type, declared_goal, status, session_id |
| dashclaw_invoke | Execute governed capability | capability_id, declared_goal, payload |
| dashclaw_capabilities_list | Discover available APIs | category, risk_level, search |
| dashclaw_policies_list | List active policies | agent_id |
| dashclaw_wait_for_approval | Wait for human decision | action_id, timeout_seconds |
| dashclaw_session_start | Register agent session | agent_id, workspace |
| dashclaw_session_end | Close session | session_id, status, summary |
Optimal files dashclaw_optimal_files_preview | Preview optimizer output for a session | session_id |
| dashclaw_optimal_files_manifest | Generate optimal-files manifest | session_id, selections |
Session continuity dashclaw_handoff_create | Write handoff bundle for next session | bundle, agent_id, project_id |
| dashclaw_handoff_latest | Fetch latest unconsumed handoff | agent_id, project_id |
| dashclaw_handoff_consume | Mark handoff consumed (idempotent) | id, session_id |
Credential hygiene dashclaw_secret_list | List tracked secrets (metadata only) | agent_id |
| dashclaw_secret_due | Secrets coming due for rotation | within_days, agent_id |
| dashclaw_secret_mark_rotated | Mark secret rotated (operator-confirmed) | id |
Skill safety dashclaw_skill_scan | Static safety scan of skill files | skill_name, files |
Open loops dashclaw_loop_add | Register action-scoped commitment | action_id, loop_type, description |
| dashclaw_loop_list | List open/resolved loops | action_id, status, priority |
| dashclaw_loop_close | Resolve an open loop | id, resolution |
Learning + retrospection dashclaw_assumption_record | Record an unverified assumption underpinning an action | action_id, assumption, basis |
| dashclaw_learning_log | Log non-obvious decision + outcome | decision, context, outcome |
| dashclaw_learning_query | Query prior decisions/lessons | query, agent_id, limit |
| dashclaw_decisions_recent | Recent governed-action ledger | agent_id, action_type, decision, since |
Agent inbox dashclaw_inbox_list | List inbox messages + unread count | agent_id, direction, unread, type, limit |
| dashclaw_messages_mark_read | Mark inbox messages as read | message_ids, agent_id |
Behavior learning dashclaw_behavior_suggestions | Observe-only Policy Coach suggestions from recorded behavior | agent_id |
Governance posture dashclaw_posture | Read the org governance posture score + 6 dimensions + findings queue (read-only) | dimension |
| dashclaw_posture_next | The next prioritized remediation finding from the posture queue (read-only) | (none) |
| URI | Description |
|---|---|
| dashclaw://policies | Active policy set |
| dashclaw://capabilities | Available capabilities and health |
| dashclaw://agent/{agent_id}/history | Recent action history (last 50) |
| dashclaw://status | Instance health + operational metrics |
| dashclaw://code-sessions/projects | Claude Code projects with ingested session data and per-project rollups |
| dashclaw://code-sessions/sessions/{session_id} | Full detail for one ingested Code Session (session, messages, tool uses) |
Config resolution: CLI args > env vars > defaults. Three config values: url (DASHCLAW_URL, default localhost:3000), apiKey (DASHCLAW_API_KEY), agentId (DASHCLAW_AGENT_ID).
{
"mcpServers": {
"dashclaw": {
"command": "npx",
"args": ["@dashclaw/mcp-server"],
"env": {
"DASHCLAW_URL": "https://your-instance.vercel.app",
"DASHCLAW_API_KEY": "oc_live_..."
}
}
}
}mcp_servers=[{
"type": "url",
"url": "https://your-instance.vercel.app/api/mcp",
"headers": {"x-api-key": "oc_live_..."},
"name": "dashclaw"
}]Settings → Connectors → Add custom connector https://your-instance.vercel.app/api/mcp Connect → log in to DashClaw → Authorize. No API key in the UI: the instance runs its own OAuth (DCR + PKCE). Guide: docs/CLAUDE-DESKTOP-PLUGIN.md
In chat clients the connector governs cooperatively: the agent, guided by the governance skill, calls dashclaw_guard / dashclaw_invoke and records its decisions — it is not a kernel-level block, so a non-compliant model could still call a native tool without consulting guard. Hard PreToolUse blocking (fail-closed deny) is a property of the CLI hook path (Claude Code / Codex / Hermes); Cowork hard-gating is not yet verified.
@dashclaw/cli handles terminal approvals and self-host diagnostics. npm run doctor runs the same engine locally with filesystem-level fix powers.
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).
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
plugins/dashclaw/.claude-plugin/plugin.json is the Claude Code plugin manifest. Distributes the DashClaw MCP server (.mcp-claude.json) plus the dashclaw-governance and dashclaw-platform-intelligence skills as one installable bundle. Full step-by-step at /guides/claude-code.
# No clone required — the CLI downloads the hooks bundle from your instance: npm i -g @dashclaw/cli dashclaw install claude # prompts for endpoint + API key dashclaw install claude --trial # hosted signup, paste the key # Working from a repo checkout instead: npm run hooks:install
dashclaw install codex wires the same governance surface DashClaw ships for Claude Code into Codex's ~/.codex/config.toml — MCP server config, PreToolUse / PostToolUse / Stop hooks, and the governance protocol in AGENTS.md. Idempotent; re-run after every git pull. Full step-by-step at /guides/codex.
# One command from the DashClaw repo root: node cli/bin/dashclaw.js install codex --project /path/to/your/project # Optional: opt in to legacy notify config for turn-complete records node cli/bin/dashclaw.js install codex --project /path/to/your/project --include-notify # Backfill existing rollouts for analytics node cli/bin/dashclaw.js code ingest-codex --dry-run node cli/bin/dashclaw.js code ingest-codex
plugins/dashclaw/.hermes-plugin/ ships eight lifecycle hooks for Hermes Agent: pre/post tool, pre/post LLM call with per-turn governance context injection, on-session start/end with live ingest finalize, secret redaction in tool output, and subagent_stop ROI tracking. Full step-by-step at /guides/hermes.
# macOS / Linux — symlinks the plugin, appends 8 hook entries to # ~/.hermes/config.yaml between sentinel markers (idempotent). bash scripts/install-hermes-plugin.sh # Windows powershell -File scripts/install-hermes-plugin.ps1 # 4-section sanity check hermes dashclaw doctor
@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.
dashclaw-governance teaches governed agents how to use DashClaw correctly — risk thresholds, decision handling (allow / warn / block / require_approval), action recording, approval-wait protocol, and session lifecycle. Pairs with @dashclaw/mcp-server. Auto-installed by the Claude Code, Codex, and Hermes plugins; also downloadable as a standalone zip.
# Zip download from this instance: curl -O `https://<your-deployment>/downloads/dashclaw-governance.zip` # Or copy the source dir directly: cp -r public/downloads/dashclaw-governance ~/.claude/skills/ # Already auto-installed if you ran one of the plugin installers above.
dashclaw-platform-intelligence gives an agent a live reference to DashClaw's API surface, governance vocabulary, integration patterns, and troubleshooting playbooks. Regenerated from the codebase via npm run livingcode:refresh so the skill never drifts from the runtime. Distributed as a zip download, mirrored into ~/.claude/skills/ by the refresh, and shipped inside the Claude Code / Codex / Hermes plugin manifests.
# Zip download (regenerated on every livingcode refresh): curl -O `https://<your-deployment>/downloads/dashclaw-platform-intelligence.zip` # Source files: ls public/downloads/dashclaw-platform-intelligence/ # SKILL.md (auto-generated from livingcode shape) # references/ (api-surface, platform-knowledge, troubleshooting) # scripts/ (bootstrap-agent-quick, diagnose, validate-integration) # Or just run the refresh — installs to ~/.claude/skills/ automatically: npm run livingcode:refresh
const claw = new DashClaw({ baseUrl, apiKey, agentId, agentName, authToken });| Parameter | Type | Required | Description |
|---|---|---|---|
| baseUrl / base_url | string | Yes | Dashboard URL |
| apiKey / api_key | string | Yes | API Key |
| agentId / agent_id | string | Yes | Unique Agent ID |
| agentName / agent_name | string | No | Human-readable agent label stored in audit trail for attribution. Automatically included on guard() calls if not overridden. |
| authToken / auth_token | string | No | Phase 2 — JWT bearer token from your OIDC provider. When set, the server verifies the signature via JWKS and returns verification_status on every guard response; the JWT sub claim overrides agent_id in the audit record. See docs/agent-identity.md. |
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). With a non_fabrication policy active, pass `content` + `sourceOfTruth` to verify outbound text before it goes out — a violation blocks (or routes to approval) and is returned under `non_fabrication` with a signed, re-verifiable receipt.
| Parameter | Type | Required | Description |
|---|---|---|---|
| action_type | string | Yes | Proposed action type |
| risk_score | number | No | 0-100 |
| content | string | No | Outbound text to non-fabrication check (used by a non_fabrication policy) |
| sourceOfTruth | object | No | Facts the content may state: { allowedFacts, requiredFacts, forbiddenPatterns?, extract? } |
Returns: Promise<{ decision: string, reasons: string[], risk_score: number, agent_risk_score: number | null, non_fabrication?: object[] }>
const result = await claw.guard({ action_type: 'deploy', risk_score: 85 });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. Non-fabrication (optional): pass content + sourceOfTruth (Node) / content + source_of_truth (Python) to have a non_fabrication policy verify the outbound content before the action proceeds — a violation blocks or routes to approval and is recorded with a signed receipt. Session linkage (optional): pass session_id (the sess_… id from a started agent session) to attribute this action to that session, so /sessions can aggregate per-session action count, cost, and risk.
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
}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 });
}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,
});Track agent beliefs.
await claw.recordAssumption({ action_id, assumption: '...' });Get current risk signals across all agents.
Returns: Promise<{ signals: Object[] }>
const { signals } = await claw.getSignals();Report agent presence and health to the control plane. Call periodically to indicate the agent is alive.
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | No | Agent status — 'online', 'busy', 'idle'. Defaults to 'online' |
| metadata | object | No | Arbitrary metadata to include with the heartbeat |
await claw.heartbeat('online', { cycle: 42, uptime_ms: 360000 });Report active provider connections and their status. Appears in the agent's Fleet profile.
| Parameter | Type | Required | Description |
|---|---|---|---|
| connections | Array<Object> | Yes | List of { name, type, status } connection objects |
await claw.reportConnections([
{ name: 'OpenAI', type: 'llm', status: 'connected' },
{ name: 'Postgres', type: 'database', status: 'connected' },
]);Register an unresolved dependency for a decision. Open loops track work that must be completed before the decision is fully resolved.
| Parameter | Type | Required | Description |
|---|---|---|---|
| action_id | string | Yes | Associated action |
| loop_type | string | Yes | The category of the loop |
| description | string | Yes | What needs to be resolved |
await claw.registerOpenLoop(action_id, 'validation', 'Waiting for PR review');
Resolve a pending loop.
await claw.resolveOpenLoop(loop_id, 'completed', 'Approved');
Record what the agent believed to be true when making a decision.
await claw.recordAssumption({ action_id, assumption: 'User is authenticated' });Compute learning velocity (rate of score improvement) for agents.
Returns: Promise<{ velocity: Array<Object> }>
const { velocity } = await claw.getLearningVelocity();Compute learning curves per action type to measure efficiency gains.
const curves = await claw.getLearningCurves();
Fetch consolidated lessons from scored outcomes — what DashClaw has learned about this agent's performance patterns.
| Parameter | Type | Required | Description |
|---|---|---|---|
| actionType | string | No | Filter by action type |
| limit | number | No | Max 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));Record a decision/outcome into the learning ledger so the governance loop improves over time. agent_id is auto-injected from the constructor's agentId when omitted. Node SDK only.
| Parameter | Type | Required | Description |
|---|---|---|---|
| decision | string | Yes | The decision that was made |
| context | string | No | Situation the decision was made in |
| reasoning | string | No | Why this decision was chosen |
| outcome | string | No | What happened as a result |
| confidence | number | No | Confidence in the decision |
| agent_id | string | No | Overrides the constructor agentId for attribution |
Returns: Promise<{ decision: Object }>
const { decision } = await claw.recordDecision({
decision: 'Rolled back the auth-service deploy',
context: 'The new deploy raised the error rate',
reasoning: 'Faster recovery than a forward fix',
outcome: 'Error rate returned to baseline',
confidence: 0.9
});Read learned recommendations for an agent/action_type from the learning ledger. agent_id defaults to the constructor's agentId when omitted. Node SDK only.
| Parameter | Type | Required | Description |
|---|---|---|---|
| agent_id | string | No | Agent to read recommendations for (defaults to constructor agentId) |
| action_type | string | No | Filter by action type |
| include_metrics | boolean | No | Include supporting metrics in the response |
| lookback_days | number | No | Window of history to consider |
| limit | number | No | Max recommendations to return |
const recs = await claw.getLearningRecommendations({
action_type: 'deploy',
include_metrics: true,
lookback_days: 30
});Fetch rendered prompt from DashClaw.
const { rendered } = await claw.renderPrompt({
template_id: 'marketing',
variables: { company: 'Apple' }
});Prompt Library — versioned template management on top of renderPrompt. Templates hold metadata; each template has one or more versions and exactly one active version, which is what renderPrompt resolves. Create/update/delete and version mutations require an admin key. Node SDK only (no Python equivalent yet).
List prompt templates. Node SDK only.
| Parameter | Type | Required | Description |
|---|---|---|---|
| category | string | No | Filter templates by category |
Returns: Promise<{ templates: Object[] }>
const { templates } = await claw.listPromptTemplates({ category: 'marketing' });Fetch a single template by id. Node SDK only.
const template = await claw.getPromptTemplate('marketing');Create a template (admin). Node SDK only.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Template name |
| description | string | No | Human-readable description |
| category | string | No | Grouping category |
Returns: Promise<{ id, name, description, category }>
const tpl = await claw.createPromptTemplate({
name: 'Cold outreach',
description: 'First-touch sales email',
category: 'sales'
});Update a template's name, description, or category (admin). Node SDK only.
await claw.updatePromptTemplate('marketing', { category: 'growth' });Delete a template along with its versions and runs (admin). Node SDK only.
Returns: Promise<{ deleted: true }>
await claw.deletePromptTemplate('marketing');List a template's versions, newest first. Node SDK only.
Returns: Promise<{ versions: Object[] }>
const { versions } = await claw.listPromptVersions('marketing');Create a new version for a template (admin). Node SDK only.
| Parameter | Type | Required | Description |
|---|---|---|---|
| content | string | Yes | The prompt body (may contain variables) |
| model_hint | string | No | Suggested model for this version |
| parameters | object | No | Default render parameters |
| changelog | string | No | What changed in this version |
await claw.createPromptVersion('marketing', {
content: 'Write a launch post for {{company}}.',
model_hint: 'claude-sonnet',
changelog: 'Initial draft'
});Fetch a single version of a template. Node SDK only.
const version = await claw.getPromptVersion('marketing', 'v3');Activate a version (admin). Activating one version deactivates the others for that template, so it becomes the version renderPrompt resolves. Node SDK only.
await claw.activatePromptVersion('marketing', 'v3');Prompt usage analytics. Node SDK only.
| Parameter | Type | Required | Description |
|---|---|---|---|
| template_id | string | No | Scope stats to a single template |
const stats = await claw.getPromptStats({ template_id: 'marketing' });List recorded prompt runs. Node SDK only.
| Parameter | Type | Required | Description |
|---|---|---|---|
| template_id | string | No | Filter by template |
| version_id | string | No | Filter by version |
| limit | number | No | Max runs to return |
const runs = await claw.listPromptRuns({ template_id: 'marketing', limit: 20 });Create a reusable scorer definition for automated evaluation.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Scorer name |
| scorer_type | string | Yes | Type (llm_judge, regex, range) |
| config | object | No | Scorer configuration |
await claw.createScorer('toxicity', 'regex', { pattern: 'bad-word' });Dry-run a scorer config against a sample without persisting anything — no eval_scores row is written. Use it to validate a quality gate before wiring the scorer into a profile. Node SDK only.
| Parameter | Type | Required | Description |
|---|---|---|---|
| scorer_type | string | Yes | Scorer type (llm_judge, regex, range) |
| config | object | No | Scorer configuration to test |
| sample | object | No | Sample input to score |
Returns: Promise<{ preview, scorer_type, result: { score, label, reasoning, error } }>
const { result } = await claw.previewScorer({
scorer_type: 'regex',
config: { pattern: 'bad-word' },
sample: { output: 'this contains a bad-word' }
});
console.log(result.score, result.label);Define weighted quality scoring profiles across multiple scorers.
await claw.createScoringProfile({
name: 'prod-quality',
dimensions: [{ scorer: 'toxicity', weight: 0.5 }]
});Side-effect-free dry-run of a single proposed policy against recent historical actions — nothing is persisted. Use it to preview how a policy would have decided before committing it; pairs with guard() for live enforcement. Node SDK only.
| Parameter | Type | Required | Description |
|---|---|---|---|
| policy_type | string | Yes | The policy type to simulate |
| rules | object | Yes | The proposed policy rules |
| days | number | No | How many days of historical actions to evaluate against |
Returns: Promise<{ summary: { total, matches, block, warn, require_approval, allow }, matches, sample_size, window_days }>
const sim = await claw.simulatePolicy({
policy_type: 'risk_threshold',
rules: { max_risk_score: 70 },
days: 30
});
console.log(sim.summary.block, 'of', sim.summary.total, 'would block');Turns a plain-English request into guard-policy drafts. The flow is iterative and never dead-ends: a clear request returns drafts; a vague one returns a best-effort draft plus suggested clarifications; an underspecified one returns clarifications only — never an empty "be more specific" rejection. Send the answered clarifications back in answers to refine. Authored from Policies → Custom → AI generator in the dashboard. Requires an LLM provider key in Settings; without one the endpoint returns 422 with "No LLM provider configured."
Generate guard-policy drafts from natural language. dry_run (default true) previews drafts and is open to any org member; dry_run: false creates the drafts and is admin-only. The dashboard saves the reviewed/edited draft via POST /api/policies rather than creating with dry_run: false.
| Parameter | Type | Required | Description |
|---|---|---|---|
| input_text | string | Yes | Plain-English description of the policy you want (max 5000 chars) |
| dry_run | boolean | No | Preview only (default true). false creates the drafts and requires an admin key |
| answers | [{ id, value }] | No | Answers to clarifications from a prior dry-run call, used to refine the drafts |
Returns: dry_run: { drafts: [{ name, policy_type, rules, confidence }], assumptions: string[], clarifications: [{ id, question, field, suggestions: string[], multi }], warnings, input_hash }
const res = await fetch(`${baseUrl}/api/policies/generate`, {
method: 'POST',
headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
body: JSON.stringify({
input_text: 'stop my agents from deleting things I care about',
dry_run: true
})
});
const { drafts, assumptions, clarifications } = await res.json();
// drafts → a best-effort protected_path draft
// [{
// name: 'Protect critical paths from deletion',
// policy_type: 'protected_path',
// rules: { paths: ['.env', 'secrets/', 'migrations/'], action: 'block' },
// confidence: 0.6
// }]
//
// assumptions → ['Assumed "things I care about" means config and secret files']
//
// clarifications → suggested-value chip sets to tighten the draft
// [
// { id: 'paths', question: 'Which paths should be protected?', field: 'rules.paths',
// suggestions: ['.env', 'secrets/', 'migrations/', 'src/'], multi: true },
// { id: 'strictness', question: 'How strict should the guard be?', field: 'rules.action',
// suggestions: ['block', 'require approval', 'warn'], multi: false }
// ]
// Refine by sending the picked answers back:
await fetch(`${baseUrl}/api/policies/generate`, {
method: 'POST',
headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
body: JSON.stringify({
input_text: 'stop my agents from deleting things I care about',
dry_run: true,
answers: [
{ id: 'paths', value: ['.env', 'secrets/', 'migrations/'] },
{ id: 'strictness', value: 'block' }
]
})
});Send a point-to-point message or broadcast to all agents in the organization.
| Parameter | Type | Required | Description |
|---|---|---|---|
| to | string | No | Target agent ID (omit for broadcast) |
| body | string | Yes | Message content |
| type | string | No | action|info|lesson|question |
| urgent | boolean | No | Mark as high priority |
await claw.sendMessage({
to: 'scout-agent-01',
body: 'I have finished indexing the repository. You can start the analysis.',
type: 'status'
});Retrieve messages from the agent inbox with optional filtering.
| Parameter | Type | Required | Description |
|---|---|---|---|
| type | string | No | Filter by message type |
| unread | boolean | No | Only return unread messages |
| limit | number | No | Max messages to return |
Returns: Promise<{ messages, total, unread_count }>
const { messages } = await claw.getInbox({ unread: true, limit: 10 });Mark messages as read for this agent. Direct messages are marked read only for the target agent (or dashboard); broadcasts update read_by for the reading agent.
| Parameter | Type | Required | Description |
|---|---|---|---|
| messageIds | string[] | Yes | Message IDs (msg_*) to mark read |
Returns: Promise<{ updated }>
const { updated } = await claw.markRead(['msg_abc123']);Archive messages for this agent so they no longer surface in the active inbox.
| Parameter | Type | Required | Description |
|---|---|---|---|
| messageIds | string[] | Yes | Message IDs (msg_*) to archive |
Returns: Promise<{ updated }>
const { updated } = await claw.archiveMessages(['msg_abc123']);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']
});Retrieve the most recent handoff for the current agent.
Returns: Promise<Object|null>
const handoff = await claw.getLatestHandoff();
Scan untrusted input for potential prompt injection or jailbreak attempts.
| Parameter | Type | Required | Description |
|---|---|---|---|
| text | string | Yes | Untrusted 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);
}DashClaw verifies which agent took each action on three independent axes, each returned on the guard response and recorded in the decisions ledger. The current path is JWKS-verified JWTs (Phase 2 / 2b / 2c); the public-key pairing API further down remains for older (v1) integrations. Full setup guide: docs/agent-identity.md.
Attach an OIDC bearer token (or pass authToken to the SDK constructor). DashClaw fetches the issuer's keys from its /.well-known/jwks.json, verifies the signature (EdDSA, RS256–512, ES256–512), and on success overrides any body-supplied agent_id with the token's sub — proof beats self-assertion. A downed issuer fails soft to unverified and never blocks a decision.
import { DashClaw } from 'dashclaw';
const claw = new DashClaw({
baseUrl: process.env.DASHCLAW_BASE_URL,
apiKey: process.env.DASHCLAW_API_KEY,
authToken: agentJwt, // OIDC bearer token minted by your IdP
});
const { decision, verification_status } = await claw.guard({
action_type: 'deploy', risk_score: 80,
});
// verification_status: 'verified' | 'unverified' | 'expired'
// | 'failed' | 'unknown_issuer' | 'exp_too_far'Three independent axes travel back on the response, each in its own field so a downed issuer or absent claim degrades gracefully instead of hard-failing:
verification_status: who signed the token. Configure trust with DASHCLAW_ALLOWED_ISSUER and DASHCLAW_JWT_AUDIENCE.replay_status: whether the token was reused. DASHCLAW_JTI_REPLAY_PROTECTION (off / best_effort / required, default best_effort) blocks a replayed jti.act_status: whether the token is bound to this call. DASHCLAW_ACT_BINDING (default off) compares the request against the token's urn:dashclaw:act-binding claim.Predates JWKS verification and is retained for older integrations. 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.
Create an agent pairing request. The agent submits its public key and waits for operator approval.
| Parameter | Type | Required | Description |
|---|---|---|---|
| public_key | string | Yes | PEM-encoded RSA public key |
| algorithm | string | No | Key algorithm. Default: RSASSA-PKCS1-v1_5 |
| agent_name | string | No | Human-readable label for the agent |
Returns: { pairing: { id, status, agent_name, created_at } }
// Node SDK — pairing is on the deprecated dashclaw/legacy subpath
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_...List all pairing requests for the organization. Admin API key required.
Returns: { pairings: Array<{ id, status, agent_name, created_at, approved_at }> }
const res = await fetch('/api/pairings', {
headers: { 'x-api-key': adminApiKey }
});
const { pairings } = await res.json();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 } }
// Node SDK (v1 legacy) const status = await claw.getPairing(pairingId); console.log(status.pairing.status); // pending | approved | expired
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 } }
const res = await fetch(`/api/pairings/${pairingId}/approve`, {
method: 'POST',
headers: { 'x-api-key': adminApiKey }
});Directly register an agent's public key as a trusted identity. Admin API key required. Bypasses the pairing flow.
| Parameter | Type | Required | Description |
|---|---|---|---|
| agent_id | string | Yes | Unique agent identifier |
| public_key | string | Yes | PEM-encoded RSA public key |
| algorithm | string | No | Key algorithm. Default: RSASSA-PKCS1-v1_5 |
Returns: { identity: { agent_id, algorithm, created_at } }
// Node SDK (v1 legacy)
await claw.registerIdentity('agent-007', publicKeyPem, 'RSASSA-PKCS1-v1_5');List all registered agent identities for the organization. Admin API key required.
Returns: { identities: Array<{ agent_id, algorithm, created_at }> }
// Node SDK (v1 legacy)
const { identities } = await claw.getIdentities();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 }
const res = await fetch(`/api/identities/${agentId}`, {
method: 'DELETE',
headers: { 'x-api-key': adminApiKey }
});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.
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 }> }
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_fromFive-state terminal outcome on every action — closes the audit-trail gap between "what was approved" and "what actually completed." See durable-execution-finality.md.
Record the terminal outcome of an approved action. One-shot: the first successful POST wins, subsequent POSTs return 409 with the current state. status must be one of completed | partial | failed. error_message is required when status=failed; progress (object) is required when status=partial. lost_confirmation is reserved for the system sweep.
Returns: { outcome: { action_id, status, outcome_at, summary, error_message, progress, elapsed_ms }, security: { clean, findings_count } }
await fetch(`${baseUrl}/api/actions/${actionId}/outcome`, {
method: 'POST',
headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
body: JSON.stringify({
status: 'completed',
summary: 'Deployed dashclaw 2.13.4 to production'
})
});Read the current outcome state. Returns the full outcome shape including elapsed_ms (outcome_at − created_at, or now − created_at while still pending). Agents call this before retrying to avoid re-executing already-completed actions.
Returns: { action_id, status, outcome_at, summary, error_message, progress, elapsed_ms }
const outcome = await fetch(
`${baseUrl}/api/actions/${actionId}/outcome`,
{ headers: { 'x-api-key': apiKey } }
).then(r => r.json());
// completed → SKIP, failed | lost_confirmation → RETRY,
// pending → WAIT, partial → CLEANUP_THEN_RETRYPackage a repeatable operational pattern as a reusable, versioned asset linking policies, prompts, knowledge, capabilities, and a model strategy.
List all workflow templates for the current org. Supports ?status=draft|active|archived, ?limit, ?offset.
const { templates } = await fetch(`${baseUrl}/api/workflows/templates`, {
headers: { 'x-api-key': apiKey }
}).then(r => r.json());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.
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'
})
});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.
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' }]
})
});Clone a template as a new draft (version resets to 1, status='draft'). Accepts optional name and slug overrides in the body.
const { template } = await fetch(
`${baseUrl}/api/workflows/templates/${templateId}/duplicate`,
{ method: 'POST', headers: { 'x-api-key': apiKey } }
).then(r => r.json());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 } }
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}`);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 }
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`)
);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 }] }
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}`)
);Reusable provider/model strategy records (primary + fallback chain, cost/latency sensitivity, budget cap). Linked from workflow templates and snapshotted at launch.
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.
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']
}
})
});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.
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 } })
});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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| messages | Array<{ role, content }> | Yes | Chat messages (system, user, assistant) |
| max_tokens | number | No | Max output tokens (default 1024) |
| temperature | number | No | Sampling temperature (default 0.7) |
| task_mode | string | No | Override 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 }
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 failedLightweight metadata layer for knowledge sources that workflows and agents can bind to. No embedding or retrieval in Phase 1 — metadata + tags only.
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.
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']
})
});Fetch or update a collection's metadata (name, description, source_type, tags, ingestion_status).
const { collection } = await fetch(
`${baseUrl}/api/knowledge/collections/${collectionId}`,
{ headers: { 'x-api-key': apiKey } }
).then(r => r.json());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.
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'
})
}
);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 } }
// SDK
const { sync } = await claw.syncKnowledgeCollection(collectionId);
console.log(sync.ingested, sync.chunks_created);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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| query | string | Yes | Natural language search query |
| limit | number | No | Max results (default 5, max 20) |
Returns: { query, collection_id, results: Array<{ chunk_id, item_id, content, score, position, token_count, title, source_uri }>, count }
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)}`));Delete a collection (and its items/chunks). Node SDK only.
Returns: Promise<{ deleted, collection_id }>
const { deleted, collection_id } = await claw.deleteKnowledgeCollection(collectionId);Governed registry of callable capabilities with risk, approval, health, and (future) pricing metadata. Workflow templates can reference capabilities by id or by tag.
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.
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'
})
});Fetch, update, or delete a capability. PATCH validates risk_level and source_type enums on change. DELETE removes the capability (SDK: claw.deleteCapability(capabilityId)).
await fetch(`${baseUrl}/api/capabilities/${capabilityId}`, {
method: 'PATCH',
headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
body: JSON.stringify({ health_status: 'degraded' })
});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).
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.
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 }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.
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_statusFetch 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.
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_msFetch 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.
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_msFetch 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).
| Parameter | Type | Required | Description |
|---|---|---|---|
| days | number | No | Rolling 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 }
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_totalList 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| decision | string | No | Filter by outcome: allow | block | flag |
| agent_id | string | No | Filter to a specific agent |
| limit | number | No | Page size (max 200, default 50) |
| offset | number | No | Pagination offset (default 0) |
Returns: { decisions: Array<{ id, agent_id, action_type, decision, matched_policies, declared_goal, agent_name, created_at }>, total, stats }
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_goalFetch 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| agentId | string | Yes | The 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 }
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_summaryPer-agent trust vectors computed from your own governed decisions (actions, guard outcomes, evaluations, feedback). Time-decayed (90-day half-life) and Bayesian-smoothed; risk_score wraps the existing 0-100 risk numbers. Each vector can be returned with an Ed25519-signed receipt that re-verifies against the instance JWKS. All reads are org-scoped.
Current reputation vector (stored snapshot, or computed read-only when none exists yet). Returns 404 for an unknown agent.
const { vector } = await claw.getAgentReputation('agent_42');
// vector: { reliability_score, completion_rate, policy_violation_rate, approval_adherence,
// quality_score, risk_score, volume_weight, confidence, total_events, last_event_at, computed_at }Recompute the vector from evidence, persist the snapshot, and store a signed receipt.
await claw.recomputeAgentReputation('agent_42');Paginated reputation events for an agent (org-scoped).
await claw.listAgentReputationEvents('agent_42', { limit: 50, offset: 0 });Signed receipt for the current vector (stored, or built read-only).
const { receipt } = await claw.getAgentReputationReceipt('agent_42');Verify a reputation receipt against the instance's published signing keys. The vector hash is checked constant-time and the Ed25519 signature is verified. Returns { ok, kid?, reason? }.
const { ok } = await claw.verifyReputationReceipt(receipt);Register external, org-owned providers that group existing capabilities and are invoked through governance. An invocation routes through the existing capability runtime (auth, timeout, retry, request/response mapping, SSRF defense), the guard, and the action ledger; the registry never reimplements HTTP. Risk derives from risk_class + budget + capability metadata via the existing risk map and predictive risk. x402 and auth metadata are recorded; no payment settlement is performed.
Register an external provider. GET /api/agents/registry lists them; GET/PATCH /api/agents/registry/:id read and update one.
const { registered_agent } = await claw.registerAgent({ name: 'Pricing API', endpoint: 'https://pricing.example.com', auth_type: 'bearer', risk_class: 'high', default_budget_usd: 5 });
await claw.addAgentCapability(registered_agent.entry_id, 'cap_123');Invoke a capability through a registered agent, governed end to end by the existing capability runtime + guard + action ledger. Returns 403 when guard blocks, 202 when approval is required, and records a thin invocation referencing the resulting action_id.
const out = await claw.invokeRegisteredAgent({
registered_agent_id: registered_agent.entry_id,
capability_id: 'cap_123',
agent_id: 'agent-1',
payload: { q: 'sku-9' }
});
// out.success, out.action_id, out.risk_score, out.resultRegister x402 providers, govern individual purchases through the guard loop, and record spend for audit. The agent executes the actual x402 call itself — DashClaw registers providers, governs purchase intent, and keeps a tamper-evident ledger. DashClaw never holds a wallet.
List registered x402 providers (org-scoped). Filter by status.
const { providers } = await claw.listProviders({ status: 'active' });Register a paid x402 provider. Supply name, category, and optional base_url, description, pricing_model, or metadata.
const { provider } = await claw.createProvider({
name: 'Exa Search',
category: 'research',
base_url: 'https://api.exa.ai',
});Provider detail including registered endpoints.
const { provider } = await claw.getProvider(provider.provider_id);Update a provider (name, status, pricing_model, metadata, etc.).
await claw.updateProvider(provider.provider_id, { status: 'disabled' });List the endpoints registered under a provider.
const { endpoints } = await claw.listProviderEndpoints(provider.provider_id);Add an endpoint to a provider. Supply name, endpoint_url, and optional default_price, sensitivity_level, or metadata.
await claw.createProviderEndpoint(provider.provider_id, {
name: 'Search',
endpoint_url: 'https://api.exa.ai/search',
default_price: 0.01,
sensitivity_level: 'low',
});Govern + record a paid acquisition. Routes through the guard loop. Required: agent_id, provider, declared_goal, purchase_reason, context_gap, expected_value. Returns { action, purchase, decision }; branch on action.status (running | pending_approval).
| Parameter | Type | Required | Description |
|---|---|---|---|
| agent_id | string | Yes | Identifier of the agent making the purchase |
| provider | string | Yes | Provider id from createProvider |
| declared_goal | string | Yes | The agent's goal that requires this purchase |
| purchase_reason | string | Yes | Why this purchase is necessary |
| context_gap | string | Yes | What information the agent lacks locally |
| expected_value | string | Yes | What value the agent expects to get |
Returns: { action: { id, status }, purchase: { id }, decision: { decision, risk_score } }
const { action, purchase, decision } = await claw.recordPurchase({
agent_id: 'research-agent',
provider: provider.provider_id,
declared_goal: 'Find recent papers on quantum computing',
purchase_reason: 'Context gap: no local data for period 2025-01-01..2026-01-01',
context_gap: 'No papers in knowledge base for the requested window',
expected_value: 'Retrieve 10+ relevant citations',
});
if (action.status === 'pending_approval') {
await claw.waitForApproval(action.id);
}
// Agent now executes the x402 call, then records the resultList governed purchases (org-scoped). Filter by provider_id.
const { purchases } = await claw.listPurchases({ provider_id: provider.provider_id });Attach the x402 result snapshot to its purchase action. Reuses the existing artifacts endpoint; links by source_action_id so the snapshot appears in that action's evidence bundle. Python callers post directly to POST /api/artifacts with artifact_type='x402_purchase_result'.
| Parameter | Type | Required | Description |
|---|---|---|---|
| actionId | string | Yes | The act_ id returned by recordPurchase |
| result | object | Yes | { summary?, data?, url? } — snapshot of what the x402 call returned |
await claw.recordPurchaseResult(action.id, {
summary: 'Found 14 papers on quantum computing',
data: { count: 14, citations: ['...'] },
url: 'https://api.research.example.com/results/...',
});Convenience: record a SETTLED x402 payment end-to-end in one call — govern + record the purchase, mark it succeeded, and (when given) attach the on-chain receipt. Use this for the pay-outside-a-hook self-report pattern: your agent pays through a native shell / wrapper that OpenClaw's hooks never see, so it must report the spend itself. The server resolves/auto-registers the provider from `provider`, so you don't register one first. Python parity: record_x402_purchase().
| Parameter | Type | Required | Description |
|---|---|---|---|
| agent_id | string | Yes | Identifier of the agent that paid |
| provider | string | Yes | Provider name/origin, e.g. "stableenrich.dev" — the server resolves it to a provider_id |
| spend | number | Yes | Settled USD amount (> 0) |
| transaction_hash | string | No | On-chain tx hash, attached as receipt evidence |
| request_id | string | No | Provider request id, attached as receipt evidence |
Returns: { action, purchase, decision, outcome }
const settled = await claw.recordX402Purchase({
agent_id: 'research-agent',
provider: 'stableenrich.dev', // name/origin
spend: 0.007, // settled USD
transaction_hash: '0xabc…',
request_id: 'req_123',
});A read-only operator aggregation over already-stored cost. It reconciles spend across surfaces — agent LLM cost, governed x402 purchases, and Claude Code sessions — without doing any new pricing. There is no SDK wrapper; this is a dashboard endpoint that powers the /spend, /spend/x402, and /spend/code UI surfaces.
Aggregate spend for the org under one lens. Sums cost already recorded by the governance runtime; it owns no tables and introduces no new pricing.
| Parameter | Type | Required | Description |
|---|---|---|---|
| lens | string | No | fleet | claude-code. Default fleet. fleet covers agent LLM cost + x402; claude-code covers Code Sessions cost. |
| period | string | No | 7d | 30d | 90d. Default 30d. |
Returns: fleet: { lens, period, agent, x402, fleet_total_usd }; claude-code: { lens, period, code_sessions, code_total_usd }
// Operator dashboard endpoint — no SDK wrapper
const res = await fetch('/api/finops/spend?lens=fleet&period=30d', {
headers: { 'x-api-key': process.env.DASHCLAW_API_KEY },
});
const { lens, period, agent, x402, fleet_total_usd } = await res.json();
// Your Claude Code spend (advisory)
const code = await fetch('/api/finops/spend?lens=claude-code&period=7d', {
headers: { 'x-api-key': process.env.DASHCLAW_API_KEY },
}).then((r) => r.json());
// code.code_sessions, code.code_total_usdA gaming-resistant, read-only governance posture score for the org: it measures what the fleet actually GOVERNS versus what it COULD, across six dimensions, and drives a human-gated remediation loop. The score only rises from ACTIVE, proven-to-fire policies — drafting a fix never raises it. Operator surface only; no SDK wrapper. Powers the /posture page. All routes experimental.
Compute the current posture score with its six dimension breakdowns, the prioritized findings queue, a summary, and the recent snapshot trend.
Returns: { score, status, cappedBy, dimensions, findings, summary, snapshots, snapshotTs }
const res = await fetch('/api/posture', {
headers: { 'x-api-key': process.env.DASHCLAW_API_KEY },
});
const { score, status, dimensions, findings, summary, snapshots } = await res.json();The prioritized remediation queue, plus the risk-accepted ledger and per-status counts. Filter by status or dimension.
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | No | open | drafted | resolved | snoozed | accepted_risk |
| dimension | string | No | Filter to one of the six posture dimensions |
Returns: { findings, riskAccepted, counts }
const res = await fetch('/api/posture/findings?status=open', {
headers: { 'x-api-key': apiKey },
});
const { findings, riskAccepted, counts } = await res.json();Human-gated resolution. action='create_draft' inserts an INACTIVE policy draft (never auto-activates, never raises the score); 'snooze' defers it; 'accept_risk' records an explicit acceptance in the ledger. Draft-only by design — a human still activates any policy at /policies.
| Parameter | Type | Required | Description |
|---|---|---|---|
| action | string | Yes | create_draft | snooze | accept_risk |
| note | string | No | Operator note (e.g. a risk-acceptance justification) |
Returns: { resolved, action, status, policy?, state, finding? }
const res = await fetch('/api/posture/findings/' + key + '/resolve', {
method: 'POST',
headers: { 'x-api-key': apiKey, 'content-type': 'application/json' },
body: JSON.stringify({ action: 'create_draft' }),
});Recompute the posture score and persist a trend snapshot for history.
Returns: { score, status, dimensions, snapshot, summary }
const res = await fetch('/api/posture/scan', {
method: 'POST',
headers: { 'x-api-key': apiKey },
});
const { score, snapshot } = await res.json();Also exposed read-only over MCP — tools dashclaw_posture + dashclaw_posture_next — and as CLI commands dashclaw posture / dashclaw next / dashclaw posture resolve <key> (draft-only). The dashboard view is at /posture.
Ingest Claude Code (and Codex) transcripts, price the spend with cache-aware accounting, surface optimizer signals (stuck loops, cache crater, context gaps), and distill a session into an Optimal Files bundle — a root CLAUDE.md, path-scoped rules, hooks, and skill packs you apply locally. The canonical parser runs server-side, so clients never parse transcripts; all routes are experimental.
Three ways in, all landing on the same server-side parser:
DASHCLAW_CODE_SESSIONS_ENABLED=1 for the Claude Code hooks. After each turn the reporter POSTs the JSONL delta with source_host: 'hook' — fail-silent if the instance is unreachable. Run node scripts/install-hooks.mjs --global to capture every project on your machine (capture-only; no API key in global config).dashclaw code ingest [--dry-run] walks ~/.claude/projects; dashclaw code ingest-codex walks ~/.codex/sessions. Large transcripts are gzip-compressed on the wire automatically, so real-world sessions stay under the 4.5 MB request limit; files over 40 MB are skipped.POST /api/code-sessions/ingest-jsonl (below), or POST /api/code-sessions/ingest-live for per-turn incremental append with finalize: true to close the session.Ingest a Claude Code JSONL transcript (or a delta). The server dedups duplicate usage fragments (Claude Code repeats one model request across many rows), computes cache-aware cost, and runs optimizer + alert detection. Accepts raw lines, a raw-gzip body (x-dashclaw-encoding: gzip header — the primary path for large transcripts), or a legacy base64 compressed_jsonl field (50 MB decompressed cap, 200k lines).
| Parameter | Type | Required | Description |
|---|---|---|---|
| project | object | Yes | { slug, source_host: "hook" | "jsonl" } |
| jsonl_lines | string[] | No | Raw JSONL lines. Either this or compressed_jsonl is required. |
| compressed_jsonl | string | No | base64(gzip(jsonl)) alternative for large transcripts. |
| session_uuid | string | No | Validated against the parser-derived uuid; mismatch is rejected. |
| tool_use_action_map | object | No | Maps tool_use ids to governed action_ids (the governance bridge). |
Returns: { project: { id, slug }, session: { session_uuid, inserted_messages, inserted_tool_uses, signals_inserted, alerts_inserted }, parser: { jsonl_records, model_requests, duplicate_fragments_skipped } }
// Backfill is easiest via the CLI: dashclaw code ingest
// Direct API — you supply the raw JSONL lines:
const res = await fetch(baseUrl + '/api/code-sessions/ingest-jsonl', {
method: 'POST',
headers: { 'x-api-key': apiKey, 'content-type': 'application/json' },
body: JSON.stringify({
project: { slug: 'my-repo', source_host: 'jsonl' },
jsonl_lines: lines, // string[] — or compressed_jsonl (base64 gzip)
}),
});
const { session, parser } = await res.json();
// parser.duplicate_fragments_skipped = the cache-aware dedup that
// prevents the Nx cost over-count from repeated usage fragmentsDistill a session into a curated config bundle (root CLAUDE.md, path-scoped rules, hooks, skill candidates), persist the selected files as a manifest, then apply it to disk with the CLI. The server cannot read your filesystem, so generation is preview-only until you apply; every file is secret-redacted before it leaves the server.
Generate the candidate file bundle for a session (read-only). Each file carries a kind, a commit recommendation, a secret-scan result, and an overwrite_risk of 'unknown' (the server can't see your working tree).
Returns: { session_id, bundle: [{ path, kind, title, content, commit_recommendation, secret_scan, overwrite_risk }], groups, analysis }
const res = await fetch(
baseUrl + '/api/code-sessions/sessions/' + sessionId + '/optimal-files/preview',
{ method: 'POST', headers: { 'x-api-key': apiKey } }
);
const { bundle } = await res.json(); // every file already secret-redactedPersist a chosen subset of the bundle as an apply-able manifest (24 h TTL, strict path allowlist: CLAUDE.md, .claude/rules/, .claude/hooks/, .claude/skills/). Returns a ready-to-run apply command.
| Parameter | Type | Required | Description |
|---|---|---|---|
| selections | Array<{ path }> | Yes | Paths chosen from the preview bundle. Paths outside the allowlist are rejected. |
Returns: { manifest_id, expires_at, apply_command }
const res = await fetch(
baseUrl + '/api/code-sessions/sessions/' + sessionId + '/optimal-files/manifest',
{
method: 'POST',
headers: { 'x-api-key': apiKey, 'content-type': 'application/json' },
body: JSON.stringify({
selections: [{ path: 'CLAUDE.md' }, { path: '.claude/rules/testing.md' }],
}),
}
);
const { manifest_id, apply_command } = await res.json();
// apply_command, e.g.: dashclaw code apply <manifest_id> --dest=. --yesRead surfaces over ingested sessions (all GET unless noted, x-api-key required):
/api/code-sessions/projects — projects with per-project cost rollups./api/code-sessions/projects/:projectId/sessions — sessions for a project./api/code-sessions/sessions/:sessionId — token in/out + cache breakdown, cache-hit %, and cost reconciliation (flags a ≥2× divergence from Mission Control pricing)./api/code-sessions/sessions/:sessionId/autopsy — outcome classification (completed / thrashed / fell_back_to_rules / timed_out / aborted) and where the spend went by tool category./api/code-sessions/subagent-roi — keep / trim / drop per subagent by success-rate and cost-per-success./api/code-sessions/memos + POST /memos/regenerate — weekly spend memo (7-day vs prior-7-day)./api/code-sessions/alerts + POST /alerts/read-all — cost-anomaly / cache-crater / stuck-loop alerts./api/learning/code-signals — optimizer findings aggregated into the learning loop.Also exposed over MCP — tools dashclaw_optimal_files_preview + dashclaw_optimal_files_manifest, and resources dashclaw://code-sessions/projects + dashclaw://code-sessions/sessions/{session_id}. The dashboard view is at /code-sessions.
Operator-facing routes exposed only when DASHCLAW_HOSTED=true. These are not SDK methods — they produce the API key that downstream SDKs consume. Self-host deploys are unaffected; all routes return 404 when the flag is unset.
Mint a new trial workspace. Public, gated by DASHCLAW_HOSTED flag + Turnstile + IP rate limit. Returns the workspace ID, a one-time API key, and onboarding URL.
| Parameter | Type | Required | Description |
|---|---|---|---|
| turnstile_token | string | No | Cloudflare Turnstile challenge token. Required in production; omit in dev bypass mode. |
Returns: { workspace_id, api_key, endpoint, expires_at, trial_action_cap, key_prefix, next_steps_url }
curl -X POST https://hosted.example.com/api/hosted/workspaces \
-H "content-type: application/json" \
-d '{"turnstile_token": "..."}'
# → { "workspace_id": "org_...", "api_key": "oc_live_...", "endpoint": "...",
# "expires_at": "...", "trial_action_cap": 10000, "key_prefix": "oc_live_",
# "next_steps_url": "https://hosted.example.com/connect?hosted=org_..." }Admin: inspect a trial workspace. Requires an admin-role API key.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Workspace (org) ID, e.g. org_abc |
Returns: { workspace_id, status, expires_at, actions_used, trial_action_cap, created_at }
curl https://hosted.example.com/api/hosted/workspaces/org_abc \ -H "x-api-key: <admin_key>"
Admin: manually delete a trial workspace and revoke its API key.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Workspace (org) ID to delete |
Returns: { deleted: true, workspace_id }
curl -X DELETE https://hosted.example.com/api/hosted/workspaces/org_abc \ -H "x-api-key: <admin_key>"
Cron-safe sweeper for expired trial workspaces. Accepts admin-role API key OR X-Cleanup-Secret header. Safe to run repeatedly — idempotent.
| Parameter | Type | Required | Description |
|---|---|---|---|
| X-Cleanup-Secret | header | No | Shared secret set via HOSTED_CLEANUP_SECRET env var. Alternative to admin API key. |
Returns: { swept: number, workspace_ids: string[] }
curl -X POST https://hosted.example.com/api/hosted/cleanup \ -H "X-Cleanup-Secret: $HOSTED_CLEANUP_SECRET"
{ message: "Validation failed", status: 400 }