Dispatch
API Reference

WebSocket Protocol

Workers talk to the coordinator over WebSocket. The connection runs on the same port as the HTTP server (4010 for Monad, 4020 for Solana).

Connection

Workers connect to the coordinator's WebSocket endpoint by upgrading the HTTP connection:

ws://localhost:4010   (Monad)
ws://localhost:4020   (Solana)

The coordinator uses Node.js server.on('upgrade') to handle WebSocket upgrades on the same HTTP server.

Message format

All messages are JSON-encoded strings. Each message has a type field that identifies the message kind.


Worker to Coordinator

register

Sent right after connection. Declares the worker's identity, type, and capabilities.

{
  "type": "register",
  "provider_pubkey": "a1b2c3d4e5f6...",
  "provider_type": "DESKTOP",
  "capabilities": ["LLM_INFER", "TASK"],
  "pricing_hint": "$0.010"
}
FieldTypeRequiredDescription
type"register"YesMessage type
provider_pubkeystringYesWorker's ed25519 public key (hex)
provider_type"DESKTOP" or "SEEKER"YesWorker type
capabilitiesJobType[]YesArray of supported job types
pricing_hintstringNoOptional pricing hint for the coordinator

heartbeat

Sent every 10 seconds to keep the connection alive and report metrics.

{
  "type": "heartbeat",
  "provider_pubkey": "a1b2c3d4e5f6...",
  "metrics": {
    "cpu_pct": 45.2,
    "mem_pct": 62.1,
    "active_jobs": 1
  }
}
FieldTypeRequiredDescription
type"heartbeat"YesMessage type
provider_pubkeystringYesWorker's public key
metricsobjectNoOptional system metrics
metrics.cpu_pctnumberNoCPU usage percentage
metrics.mem_pctnumberNoMemory usage percentage
metrics.active_jobsnumberNoCurrently running jobs

job_complete

Sent when a worker finishes a job. Includes the output and a bundled receipt for atomic storage on the coordinator.

{
  "type": "job_complete",
  "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "output": {
    "sentiment": "positive",
    "confidence": 0.5
  },
  "output_hash": "sha256:abcdef1234567890...",
  "receipt": {
    "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "provider_pubkey": "a1b2c3d4e5f6...",
    "output_hash": "sha256:abcdef1234567890...",
    "completed_at": "2026-02-09T12:00:05.000Z",
    "payment_ref": null
  },
  "receipt_signature": "base64-encoded-ed25519-signature"
}
FieldTypeRequiredDescription
type"job_complete"YesMessage type
job_idstringYesThe assigned job ID
outputunknownYesJob result (shape depends on job type)
output_hashstringYesSHA-256 hash of JSON-serialized output
receiptobjectNoBundled receipt for atomic storage
receipt_signaturestringNoBase64 ed25519 signature over canonical JSON of receipt

job_reject

Sent when a worker cannot execute an assigned job.

{
  "type": "job_reject",
  "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "reason": "Ollama not available"
}
FieldTypeRequiredDescription
type"job_reject"YesMessage type
job_idstringYesThe rejected job ID
reasonstringYesHuman-readable rejection reason

receipt_submit

Alternative to bundling the receipt in job_complete. Sends a receipt separately.

{
  "type": "receipt_submit",
  "receipt": {
    "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "provider_pubkey": "a1b2c3d4e5f6...",
    "output_hash": "sha256:abcdef1234567890...",
    "completed_at": "2026-02-09T12:00:05.000Z",
    "payment_ref": null
  },
  "signature": "base64-encoded-ed25519-signature"
}
FieldTypeRequiredDescription
type"receipt_submit"YesMessage type
receiptobjectYesThe receipt object
receipt.job_idstringYesJob ID this receipt covers
receipt.provider_pubkeystringYesWorker's public key
receipt.output_hashstringYesSHA-256 hash of the output
receipt.completed_atstringYesISO 8601 completion timestamp
receipt.payment_refstring | nullYesOnchain payment reference (null if none)
signaturestringYesBase64 ed25519 signature

Coordinator to Worker

register_ack

Sent in response to a register message.

{
  "type": "register_ack",
  "status": "ok",
  "worker_id": "worker_abc123"
}
FieldTypeDescription
type"register_ack"Message type
status"ok" or "error"Registration result
worker_idstringAssigned worker ID (on success)
errorstringError message (on failure)

heartbeat_ack

Sent in response to a heartbeat message.

{
  "type": "heartbeat_ack",
  "status": "ok"
}

job_assign

Sent when the coordinator assigns a job to this worker.

{
  "type": "job_assign",
  "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "job_type": "TASK",
  "payload": {
    "task_type": "classify",
    "input": "This product is amazing."
  },
  "policy": "CHEAP",
  "privacy_class": "PUBLIC",
  "user_id": "user_abc123"
}
FieldTypeDescription
type"job_assign"Message type
job_idstringUnique job identifier
job_type"LLM_INFER" or "TASK"Job type
payloadobjectJob payload (varies by job type)
payload.promptstringLLM prompt (LLM_INFER only)
payload.max_tokensnumberMax tokens (LLM_INFER only)
payload.task_typestringTask type (TASK only)
payload.inputstringTask input (TASK only)
policystringPricing tier used
privacy_class"PUBLIC" or "PRIVATE"Privacy classification
user_idstringID of the user who submitted the job

error

Sent when the coordinator hits an error related to this worker.

{
  "type": "error",
  "code": "invalid_message",
  "message": "Failed to parse WebSocket message"
}
FieldTypeDescription
type"error"Message type
codestringMachine-readable error code
messagestringHuman-readable error description

Union types

For TypeScript consumers, the protocol package exports union types:

type WorkerToCoordinator =
  | RegisterMsg
  | HeartbeatMsg
  | JobCompleteMsg
  | JobRejectMsg
  | ReceiptSubmitMsg;

type CoordinatorToWorker =
  | RegisterAckMsg
  | HeartbeatAckMsg
  | JobAssignMsg
  | ErrorMsg;

type WSMessage = WorkerToCoordinator | CoordinatorToWorker;

Import from @dispatch/protocol:

import type {
  RegisterMsg,
  HeartbeatMsg,
  JobCompleteMsg,
  JobAssignMsg,
  WSMessage,
} from "@dispatch/protocol";