Docs

Webhooks

Receive real-time notifications when runs complete, fail, or are cancelled

Overview

Instead of polling GET /v1/runs/:id, you can have coSPEC push notifications to your server when runs reach a terminal state.

There are two mechanisms:

MethodScopeSigning
Org webhooksAll runs in your orgHMAC-SHA256
Per-run callbackUrlSingle runNone

This guide covers both, starting with org webhooks.

Webhooks are currently API-only — there is no UI for managing them in the dashboard yet.

Registering a Webhook

Terminal
curl -X POST https://api.cospec.io/v1/webhooks \
  -H "Authorization: Bearer csk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks/cospec",
    "events": ["run.completed", "run.failed", "run.cancelled"]
  }'

Response:

{
  "id": "wh_abc123",
  "url": "https://your-server.com/webhooks/cospec",
  "events": ["run.completed", "run.failed", "run.cancelled"],
  "secret": "whsec_dGhpcyBpcyBhIHNhbXBsZSBzZWNyZXQ=",
  "createdAt": "2026-01-15T10:00:00Z"
}

The secret is shown only once in the creation response. Store it immediately — you'll need it to verify signatures.

Constraints:

  • URL must use HTTPS
  • Maximum 25 webhooks per organization
  • Each (org, url) pair must be unique

Attempting to exceed the limit returns WEBHOOK_LIMIT_EXCEEDED. Registering a duplicate URL returns WEBHOOK_URL_DUPLICATE.

Events

EventFires when
run.completedAgent finished successfully
run.failedAgent hit a guardrail or encountered an error
run.cancelledRun was cancelled via the API

Payload

Every webhook delivery sends a JSON body with the event type, timestamp, and the full run object:

{
  "event": "run.completed",
  "timestamp": "2026-01-15T10:35:00Z",
  "run": {
    "id": "run_abc123",
    "status": "completed",
    "prompt": "Refactor the auth middleware to use JWT",
    "repo": "your-org/your-repo",
    "outputs": [
      {
        "type": "pr",
        "url": "https://github.com/your-org/your-repo/pull/42",
        "title": "Refactor auth middleware to use JWT",
        "number": 42
      }
    ],
    "usage": { "totalCostUsd": 0.42 },
    "createdAt": "2026-01-15T10:30:00Z",
    "completedAt": "2026-01-15T10:35:00Z"
  }
}

The run object matches the response from GET /v1/runs/:id.

Delivery Headers

Each webhook request includes the following headers:

HeaderDescriptionExample
webhook-idUnique delivery ID (for idempotency)msg_2Kj9s...
webhook-timestampUnix timestamp of the delivery1737024900
webhook-signatureHMAC-SHA256 signature(s)v1,K5oZfz...
Content-TypeAlways application/jsonapplication/json
User-AgentSender identifiercospec-webhooks/1.0
X-Cospec-EventEvent typerun.completed
X-Cospec-Delivery-IdSame as webhook-idmsg_2Kj9s...
X-Cospec-Delivery-AttemptAttempt number (1-based)1

Retry Policy

coSPEC uses at-least-once delivery with automatic retries.

  • Attempts: 3 (initial + 2 retries)
  • Backoff: Exponential — 2 s base, 30 s max
  • Timeout: 10 s per attempt
Your server responds withcoSPEC action
2xxDelivery successful — no retry
410 GoneEndpoint decommissioned — stop permanently, disable webhook
Other 4xxClient error — stop retrying
5xx or timeoutServer error — retry with backoff

Best Practices

  • Respond 200 quickly — do heavy processing asynchronously. If your handler takes longer than 10 s, the delivery will time out.
  • Use webhook-id for idempotency — retries resend the same webhook-id. Store processed IDs and skip duplicates.
  • Return 410 for decommissioned endpoints — this tells coSPEC to stop sending and disable the webhook, keeping your account clean.

Per-Run Callbacks (callbackUrl)

For simpler use cases, you can set a callbackUrl on individual runs instead of registering an org-wide webhook:

Terminal
curl -X POST https://api.cospec.io/v1/runs \
  -H "Authorization: Bearer csk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "repo": "your-org/your-repo",
    "prompt": "Add input validation to the signup form",
    "template": "your-template",
    "model": "sonnet",
    "callbackUrl": "https://your-server.com/callback"
  }'

When the run finishes, coSPEC sends the same payload to your callback URL. The run object includes a callbackStatus field (pending, delivered, or failed) so you can check delivery status.

Key differences from org webhooks:

Org webhookscallbackUrl
ScopeAll runsSingle run
Signature verificationHMAC-SHA256None
Retries3 attempts with backoff3 attempts with backoff
RegistrationOne-time setupSet per run
Secret managementRequiredNot needed

On this page