Docs/API/Webhooks

Webhooks.

Webhooks are how wrxstack pushes events to your systems. Subscribe to one or many event types, verify the signature, write idempotent handlers, replay any delivery from the audit log.

Subscribe

curl -X POST https://api.wrxstack.com/v1/webhooks   -H "Authorization: Bearer $WRX_API_KEY"   -H "Content-Type: application/json"   -d '{
    "url":    "https://hooks.acme.com/wrxstack",
    "events": ["task.created","task.completed","assistant.run.completed"],
    "secret": "whsec_..."
  }'

Event catalog

EventPayload contains
task.createdFull task object
task.updatedTask object + changed fields
task.completedTask object + completion metadata
document.createdDocument metadata (not the body)
document.updatedDocument metadata + diff stats
assistant.run.completedRun id, status, step summary
assistant.run.failedRun id + error envelope
approval.requestedApproval id + workflow context
approval.decidedApproval id + decision + reviewer

The full event catalog (50+) is in audit log schema; webhook events are the subset we deliver externally.

Delivery format

Each delivery is a POST with two headers and a JSON body.

POST /hooks/wrxstack HTTP/1.1
Content-Type: application/json
X-Wrx-Signature: t=1716235200,v1=5d672e...e4b1
X-Wrx-Event-Id:  evt_01HQ3K...

{
  "id":        "evt_01HQ3K...",
  "event":     "task.completed",
  "workspace": "acme",
  "created_at": "2026-05-17T15:14:22Z",
  "data": { "task": { ... } }
}

Verify the signature

Compute HMAC-SHA256 of {timestamp}.{raw_body} using your shared secret. Compare to the v1= value in X-Wrx-Signature with a constant-time compare.

import hmac, hashlib

def verify(secret, sig_header, raw_body):
    parts = dict(p.split("=", 1) for p in sig_header.split(","))
    signed = f"{parts['t']}.{raw_body.decode()}"
    expected = hmac.new(secret.encode(), signed.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

Reject anything older than five minutes (replay protection). Reject anything that fails the compare.

Retries and idempotency

Failed deliveries retry with exponential backoff for 24 hours. Every retry uses the same X-Wrx-Event-Id; store it and skip duplicates.

You can replay any delivery manually:

curl -X POST https://api.wrxstack.com/v1/webhooks/{id}/replay   -H "Authorization: Bearer $WRX_API_KEY"   -d '{"event_id":"evt_01HQ3K..."}'

Errors

CodeHTTPMeaning
webhook_url_invalid400Must be HTTPS, must respond to a HEAD probe
event_unknown400Event name not in the catalog
signature_invalid401Replay or test endpoint