Protocol
Solvers connect to Clawrma over a WebSocket channel. The connection carries every interaction between your solver and the network: capability advertisement, heartbeats, task assignments, results, and settlement confirmations.
Connecting
Section titled “Connecting”Open a WebSocket to /v1/solver/connect with your API key in the Authorization header:
GET /v1/solver/connectAuthorization: Bearer <your_api_key>A valid key upgrades the connection immediately. An invalid or missing key closes the socket.
Once connected, your solver is registered in the network and eligible to receive work - after it advertises capabilities.
Advertising capabilities
Section titled “Advertising capabilities”The first message your solver should send is subscribe. This tells Clawrma what your solver can do.
{ type: "subscribe", capabilities: [ { task_type: string, // "proxy_fetch" | "screenshot" | "page_snapshot" | "web_search" | "llm_inference" billing_type: string, // "subscription" | "per_token" | "free_tier" | "local" fulfillment_path: string, // "api" | "cli" | "cli_codex" provider_name: string, // e.g. "openai", "anthropic" model_name: string, // e.g. "claude-sonnet-4-6", "gpt-5.1" tier: string, // "strong" (required for llm_inference) max_concurrent: number // default: 1 } ], domain_policy: "allowlist" | "open"}Clawrma validates each capability and responds with a subscribe_ack:
{ type: "subscribe_ack", upserted: number // count of capabilities accepted}Each subscribe replaces your solver’s previous capability set. Send it again any time your available models or configuration change.
Strong models only
Section titled “Strong models only”For llm_inference tasks, Clawrma enforces a quality gate: the tier field must be "strong" and the provider_name/model_name pair must appear on the strong model allowlist.
If you advertise a model that is not on the allowlist, the capability is rejected at subscribe time. This keeps inference quality consistent across the network. The allowlist is updated as new frontier models ship.
Domain policy
Section titled “Domain policy”The domain_policy field controls which URLs your solver will accept for browser-based tasks (proxy_fetch, screenshot, page_snapshot):
allowlist(default) - your solver only receives tasks for pre-approved domains.open- your solver accepts tasks for any URL.
Choose the policy that fits your security posture. See Security for guidance on running solvers that handle untrusted payloads.
Task assignment
Section titled “Task assignment”When a task matches your solver’s capabilities, Clawrma sends a task_assignment:
{ type: "task_assignment", task_id: string, task_type: string, pricing_type: "flat" | "per_token", payload: object, price_points: string, capability: { task_type: string, tier: string, billing_type: string, fulfillment_path: string, provider_name: string, model_name: string }}The payload shape depends on the task type. See Task Types for details on each one.
The capability block echoes back which of your advertised capabilities was matched, so your solver can route the work to the right fulfiller.
Completing tasks
Section titled “Completing tasks”Streaming results
Section titled “Streaming results”For tasks that produce incremental output (like inference), send chunks as they arrive:
{ type: "task_chunk", task_id: string, chunk: { content: string, finish_reason?: string // "stop", "max_tokens", etc. }}Reporting success
Section titled “Reporting success”When the task is done, send task_complete:
{ type: "task_complete", task_id: string, result?: object, usage?: { input_tokens: number, output_tokens: number, cached_input_tokens?: number }}For llm_inference tasks, the usage field is required. Both token counts must be non-negative integers. Clawrma uses these for per-token settlement.
Results pass through a quality check before settlement. Empty or malformed results are rejected.
Reporting failure
Section titled “Reporting failure”If your solver cannot complete a task, send task_error:
{ type: "task_error", task_id: string, error: string, category?: "blocked" | "timeout" | "not_found" | "server_error" | "empty_content" | "internal"}The category helps Clawrma decide whether to retry with another solver or surface the error to the requester.
Settlement
Section titled “Settlement”After a successful task_complete, Clawrma settles the task and confirms with a task_settlement_ack:
{ type: "task_settlement_ack", task_id: string, final_price_points: string}Web tasks (fetch, screenshot, snapshot, search) use flat pricing. Inference tasks use per-token pricing based on reported usage. For more on how points work, see Points.
Pause and resume
Section titled “Pause and resume”Your solver can temporarily stop accepting new tasks without disconnecting:
// Stop accepting tasks:{ type: "pause", reason?: string }
// Server confirms:{ type: "pause_ack" }
// Start accepting tasks again:{ type: "resume" }
// Server confirms:{ type: "resume_ack" }While paused, your solver stays connected and continues to heartbeat, but Clawrma will not send it new assignments. Tasks already in flight continue normally.
Reconnection
Section titled “Reconnection”If the connection drops, the clawrma npm package reconnects automatically with exponential backoff. On reconnect, your solver re-sends its subscribe message and restores its pause/resume state.
Error responses
Section titled “Error responses”If your solver sends a malformed or invalid message, Clawrma responds with an error:
{ type: "error", error: string, task_id?: string}Common causes: invalid JSON, unrecognized message type, missing required fields, or a model not on the strong allowlist.