Webhooks
Instead of polling the API, let Rigyx call you. Subscribe a URL to the events you care about and get a signed POST the moment each one happens.
Subscribe
Create a subscription with POST /webhooks — a URL plus the events to send it. The URL must be https:// in production. The response includes a signing secret, returned once — store it; you will need it to verify every delivery.
curl -X POST https://rigyx.com/api/public/v1/webhooks \
-H "Authorization: Bearer jmt_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Pipeline sync",
"url": "https://your-server.com/hooks/rigyx",
"events": ["lead.qualified", "campaign.completed"]
}'{
"id": "5b2e...-uuid",
"url": "https://your-server.com/hooks/rigyx",
"events": ["lead.qualified", "campaign.completed"],
"secret": "whsec_a1b2c3... ← shown once, store it now",
"isActive": true
}The event catalogue
Subscribe to any of these event types. An unknown type is rejected at subscribe time.
lead.createdlead.qualifiedlead.convertedcontact.createdcontact.updatedcampaign.createdcampaign.sentcampaign.completedemail.sentemail.openedemail.clickedemail.repliedemail.receiveddocument.draftdocument.approveddocument.sentinvoice.createdinvoice.paidinvoice.overduetask.createdtask.completedtask.failedreview.discoveredreview.approvedreview.postedadcamp.approvedadcamp.launchedadcamp.metrics_updatedagent.task_startedagent.task_completedagent.handoff_sentThe payload
Every delivery is a POST with the same JSON envelope. The event-specific fields live under data.
{
"id": "e9f1...-uuid",
"type": "lead.qualified",
"business_id": "5b2e...-uuid",
"created_at": "2026-05-22T14:22:51.512Z",
"data": {
"leadId": "a1b2...-uuid",
"companyName": "Acme Ltd",
"source": "sales_agent"
}
}Alongside the body, every delivery carries these headers:
Content-Type: application/json
User-Agent: Rigyx-Webhooks/1.0
X-Rigyx-Signature: sha256=<hex>
X-Rigyx-Timestamp: <unix seconds>
X-Rigyx-Delivery-Id: <uuid>
X-Rigyx-Event: <event type>Verify the signature
Always verify before trusting a payload. The signature is an HMAC-SHA256, keyed with your subscription secret, computed over {timestamp}.{raw_body} — the timestamp, a dot, then the exact bytes of the request body.
import crypto from 'crypto';
// Verify an inbound Rigyx webhook. Returns true if authentic.
function verifyWebhook(headers, rawBody, secret) {
const signature = headers['x-rigyx-signature'] || ''; // "sha256=<hex>"
const timestamp = headers['x-rigyx-timestamp'] || ''; // unix seconds
// Replay protection — reject anything older than 5 minutes.
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp, 10);
if (Number.isNaN(age) || age > 300) return false;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(timestamp + '.' + rawBody)
.digest('hex');
const a = Buffer.from(signature);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}import hmac, hashlib, time
def verify_webhook(headers, raw_body: bytes, secret: str) -> bool:
signature = headers.get("X-Rigyx-Signature", "") # "sha256=<hex>"
timestamp = headers.get("X-Rigyx-Timestamp", "") # unix seconds
# Replay protection — reject anything older than 5 minutes.
try:
age = int(time.time()) - int(timestamp)
except ValueError:
return False
if age > 300:
return False
signed = timestamp.encode() + b"." + raw_body
expected = "sha256=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, expected)Retries & failure handling
The first delivery is attempted immediately. If your endpoint does not answer with a 2xx, it is retried with growing backoff:
attempt 1 immediate
attempt 2 after 2 minutes
attempt 3 after 10 minutes
attempt 4 after 1 hour
attempt 5 after 6 hours
attempt 6 after 24 hours
→ still failing? delivery marked "abandoned"Each delivery is given six attempts in total. Separately, if a subscription racks up 10 consecutive failed deliveries, it is automatically disabled and the business owner is notified — so a permanently broken endpoint does not retry forever. Re-enable it from the portal once the endpoint is fixed.
2xx as soon as you have stored the event, then do the real work asynchronously. Deliveries time out after 10 seconds — if your handler is slow, the delivery counts as failed.What is never sent
Before signing, the dispatcher strips sensitive fields from every payload — API keys, secrets, tokens, passwords, raw LLM prompts and responses, and internal reasoning. A webhook payload only ever contains business data safe to leave your perimeter.
Catching up after an outage
If your endpoint is down long enough for deliveries to be abandoned, backfill from the events log rather than losing them. Poll GET /events — it is cursor-paginated — and page back to your last-known event.
curl -H "Authorization: Bearer jmt_..." \
"https://rigyx.com/api/public/v1/events?type=lead.qualified&limit=200"