Skip to main content

Outbound Webhooks

ColabHive signs and delivers an HTTPS notification when an inference task, Action invocation, training run, or training merge reaches a terminal state — so you stop polling for work that may take minutes or hours.

An event tells you when something finished and which resource changed. It does not carry the result: payloads contain no prompts, inputs, outputs, logs, or artifacts. Use data.resource_url with your normal API credentials to fetch the actual state. Polling remains fully supported and is not deprecated.

Quickstart

1. Register your destination. You get the signing secret exactly once — store it before the response leaves your screen.

curl -X POST https://api.colabhive.com/api/builder/v1/webhook-endpoints \
-H "X-API-Key: $KEY" -H "X-Account-ID: $ACCT" \
-H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" \
-d '{
"name": "production-events",
"url": "https://events.example.com/colabhive",
"event_types": ["inference.task.succeeded", "training.run.completed"]
}'

2. Answer the verification challenge. The destination starts as pending_verification and ColabHive immediately sends a signed webhook.endpoint_verification event. Echo the challenge back and the endpoint flips to active.

3. Verify every delivery before you trust it. Check the signature over the raw bytes, then deduplicate on event.id — delivery is at-least-once, so the same event can legitimately arrive twice.

from colabhive.webhooks import WebhookEvent, verify_signature   # pip install colabhive==0.7.0

raw_body = await request.body() # exact bytes, before any parsing
verify_signature(raw_body, request.headers, signing_secret)
event = WebhookEvent.from_raw_body(raw_body)

if already_processed(event.id): # at-least-once: duplicates are normal
return 200
record(event.id) # persist BEFORE side effects
return 200 # 2xx only once your work is durable

Three mistakes account for most broken receivers, and all three are silent:

  • parsing before verifying. Re-serialized JSON has a different signature even when it is semantically identical, so verify the exact bytes you received;
  • returning 2xx before the work is committed. A 2xx is a promise; ColabHive will not resend;
  • assuming exactly-once. Deduplicate on event.id, not on attempt number.

Management lifecycle

The Builder REST API under /api/builder/v1 is the authoritative management contract. Reads require the viewer role; test delivery and manual redelivery require operator; create, update, delete, and secret rotation require admin. Generic Actions do not duplicate these administrative operations: their OpenAPI metadata keeps Actions exposure at none.

MethodPathPurpose
POST/webhook-endpointsCreate a destination and reveal its whsec_… signing secret once.
GET/webhook-endpointsList redacted destinations using cursor pagination.
GET/webhook-endpoints/{endpoint_id}Read redacted configuration and its ETag.
PATCH/webhook-endpoints/{endpoint_id}Change name, destination, subscriptions, or enabled state; requires If-Match.
DELETE/webhook-endpoints/{endpoint_id}Disable and delete a destination; requires If-Match.
POST/webhook-endpoints/{endpoint_id}/rotate-secretRotate gracefully or in emergency mode.
POST/webhook-endpoints/{endpoint_id}/testQueue a signed webhook.test event.
GET/webhook-deliveriesList delivery history, newest first, with cursor pagination.
GET/webhook-deliveries/{delivery_id}Read one delivery with its append-only attempt history.
POST/webhook-deliveries/{delivery_id}/redeliverRe-send an existing event under a new generation.

Create, rotate, and test requests support Idempotency-Key. Store returned secrets immediately: normal GET/list responses expose only secret_hint, url_origin, and the fixed redaction https://***. Exact idempotent replay is bounded and does not turn GET into a secret-recovery API.

Destination verification

A new or changed URL starts as pending_verification. ColabHive sends a signed webhook.endpoint_verification event containing data.verification_challenge. Within the verification window, return HTTP 2xx with both:

  • ColabHive-Verification-Challenge: <exact challenge>; and
  • the exact compact JSON body {"verification_challenge":"<exact challenge>"}.

Only a verified active endpoint receives subscribed or test events. Redirects do not count as verification. Changing the URL invalidates the old challenge and starts verification again.

Event matrix

FamilyEvent typesResource URLOrigins
Inferenceinference.task.succeeded, inference.task.failed/api/builder/v1/tasks/{id}REST, OpenAI-compatible, MCP
Invocationinvocation.completed, invocation.failed/api/builder/v1/invocations/{id}Actions REST, MCP
Training runtraining.run.completed, training.run.failed, training.run.cancelled/api/builder/v1/training/runs/{id}Training API
Training mergetraining.merge.completed, training.merge.failed, training.merge.cancelled/api/builder/v1/training/runs/{id}Merge API

webhook.endpoint_verification and webhook.test are lifecycle events and cannot be selected as normal subscriptions. Intermediate progress, logs, metrics, queue changes, and retries do not emit public events. A failure event represents final failure after recovery/retry policy is exhausted.

Every payload uses envelope version 1:

{
"id": "event-uuid",
"object": "event",
"api_version": "1",
"type": "training.run.completed",
"created_at": "2026-08-17T12:00:00Z",
"account_id": "account-uuid",
"data": {
"resource": {"id": "run-uuid", "object": "training_run", "status": "completed"},
"resource_url": "/api/builder/v1/training/runs/run-uuid"
}
}

The canonical event names and JSON Schemas live in contracts/webhooks/v1. Consumers should ignore transport attempt count when deduplicating and use the envelope id / ColabHive-Event-Id.

Verify signatures before parsing

Deliveries include:

HeaderMeaning
ColabHive-Signaturet=<unix>,v1=<hex HMAC-SHA256>; rotation can include two v1 values.
ColabHive-Event-IdStable event/deduplication ID across delivery attempts.
ColabHive-Delivery-IdDestination-specific delivery ID.
ColabHive-AttemptOne-based transport attempt number.
ColabHive-Webhook-Version1.

Compute HMAC-SHA256 over the exact bytes str(timestamp) + "." + raw_request_body, using the full whsec_… value exactly as returned. Compare digests in constant time and reject timestamps outside your replay window. Do not parse, normalize, pretty-print, or reserialize JSON first—even equivalent JSON has a different signature. Persist event.id atomically before performing side effects.

Python SDK 0.7.0 contains verify_signature, header helpers, event models, and client.webhooks with client.webhooks.deliveries. It is published on PyPI:

pip install colabhive==0.7.0
from colabhive.webhooks import WebhookEvent, verify_signature

raw_body = await request.body()
verification = verify_signature(raw_body, request.headers, signing_secret)
event = WebhookEvent.from_raw_body(raw_body)
assert verification.event_id == event.id

Complete receiver examples ship in the documentation bundle under snippets/python and snippets/node.

SDK and MCP surfaces

The Python SDK 0.7.0 mirrors all seven management operations and supplies receiver-side verification helpers. MCP 0.3.0 (pip install colabhive-mcp==0.3.0) mirrors list, get, create, update, delete, rotate, and test while preserving the REST roles, ETag/If-Match, and Idempotency-Key contract. All seven MCP tools are hidden by default and require COLABHIVE_MCP_WEBHOOK_TOOLS_ENABLED=true. Create and rotate return a one-time signing secret and require the additional COLABHIVE_ALLOW_WEBHOOK_SECRET_TOOLS=true gate; do not expose their results to untrusted model context. The MCP surface stays opt-in even though the REST management routes are live.

Delivery semantics and retries

Delivery is at least once. Receivers must tolerate duplicates and return 2xx only after their durable work is committed. ColabHive retries timeouts, connection failures, HTTP 408/425/429, and 5xx. Other 4xx responses are permanent; HTTP 410 disables the endpoint; redirects are rejected.

The retry policy permits up to eight attempts within a 48-hour deadline, approximately at 0s, 30s, 2m, 10m, 1h, 6h, 24h, and 36h plus bounded final jitter. A valid Retry-After on 429 or 503 can delay the next attempt but never extend the deadline. Exhausted deliveries are retained in a dead_lettered state.

Delivery history and manual redelivery

GET /webhook-deliveries returns deliveries newest first and accepts endpoint_id, status, and event_type filters. The list omits attempts; GET /webhook-deliveries/{delivery_id} adds the append-only attempt history. Neither route returns the destination URL, the signing secret, the event payload, or any remote response body — an attempt exposes only its outcome, a sanitized error code, an HTTP status class, and timing.

POST /webhook-deliveries/{delivery_id}/redeliver re-sends an existing event. It keeps the same event ID and the same frozen payload, creates the next delivery_generation, and references the previous one through redelivery_of_delivery_id. It pins the destination's currently active and verified version and secrets, so it never resurrects a retired URL or key.

The operation is idempotent by structure rather than by header: while a redelivery of the same delivery is still non-terminal, repeating the request returns that one with replayed: true instead of creating another generation. It answers 409 when the destination is not active and verified, and 410 once the event is past its retention window.

A redelivery never reopens, retries, or modifies the inference, invocation, training, or merge workload that produced the event. A dead-lettered delivery is terminal for that delivery alone; the run that generated it stays exactly as it was.

curl -X POST \
"https://api.colabhive.com/api/builder/v1/webhook-deliveries/$DELIVERY_ID/redeliver" \
-H "X-API-Key: $KEY" -H "X-Account-ID: $ACCT"

Retention

WhatKept for
Events and deliveries30 days
Per-attempt history14 days

The attempt history expires before the delivery that groups it: it exists to operate a recent incident, not as an archive. A delivery you can still see may therefore show no attempts.

Once an event passes its window it is collected, and POST /webhook-deliveries/{delivery_id}/redeliver answers 410 — the payload no longer exists to re-send. Export anything you need to keep beyond that.

These windows are not configurable per account today.

Security and destination restrictions

  • Destinations must use HTTPS on port 443, without URL userinfo or fragments.
  • Literal IPs, localhost, non-global/private/link-local/metadata addresses, ColabHive-owned blocked domains, unsafe DNS results, redirects, and DNS rebinding are rejected.
  • Delivery resolves, validates, pins, connects, and revalidates the peer address. TLS verification remains enabled.
  • Destination URLs and signing secrets are encrypted at rest; APIs, logs, metrics, and audit records return only redacted projections.
  • Keep current and previous secrets only for the documented graceful-rotation overlap; emergency rotation invalidates the prior secret immediately.

Current limits

There is no public API yet for replay of an arbitrary historical event that never produced a delivery, retention configuration, or per-account delivery quotas. Redelivery operates on an existing delivery record, not on any past event you choose. The management API also does not accept a callback URL per inference/training request.

Polling GET /tasks/{id}, GET /invocations/{id} and GET /training/runs/{id} stays available and is the right fallback whenever a receiver is down or you would rather not run one.