Skip to content

Webhooks

Webhooks push real-time notifications to your server when things happen in Chronary — events created, updated, deleted, or agents modified. No polling required.

  1. You register a webhook URL and choose which event types to listen for
  2. When a matching event occurs, Chronary sends an HTTP POST to your URL
  3. The request includes an HMAC-SHA256 signature for verification
  4. Failed deliveries are automatically retried with exponential backoff
Terminal window
curl -X POST https://api.chronary.ai/v1/webhooks \
-H "Authorization: Bearer chr_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/chronary",
"events": ["event.created", "event.updated", "event.deleted"]
}'

| Event type | Trigger | |-----------|---------| | event.created | A new event is added to any calendar | | event.updated | An event’s fields are modified | | event.deleted | An event is deleted | | agent.created | A new agent is created | | agent.updated | An agent’s fields are modified |

Each delivery is an HTTP POST with a JSON body:

{
"id": "whkevt_x1y2z3",
"type": "event.created",
"timestamp": "2026-04-07T14:00:00Z",
"data": {
"calendar_id": "cal_x1y2z3",
"event": {
"id": "evt_m1n2o3",
"title": "Strategy sync with Acme Corp",
"start_time": "2026-04-07T14:00:00Z",
"end_time": "2026-04-07T14:30:00Z",
"status": "confirmed"
}
}
}

Every webhook request includes a X-Chronary-Signature header containing an HMAC-SHA256 signature of the request body.

import { createHmac, timingSafeEqual } from 'crypto';
function verifyWebhook(body, signature, secret) {
const expected = createHmac('sha256', secret)
.update(body)
.digest('hex');
return timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your request handler:
const isValid = verifyWebhook(
rawBody,
req.headers['x-chronary-signature'],
process.env.WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}

If your endpoint returns a non-2xx status or times out (30 second timeout), Chronary retries with exponential backoff:

| Attempt | Delay | |---------|-------| | 1st retry | ~1 minute | | 2nd retry | ~5 minutes | | 3rd retry | ~30 minutes | | 4th retry | ~2 hours | | 5th retry | ~8 hours |

After 5 failed retries, the delivery is marked as failed. The webhook remains active for future events.

Terminal window
curl https://api.chronary.ai/v1/webhooks \
-H "Authorization: Bearer chr_sk_your_key_here"

Change the URL, event types, or active status:

Terminal window
curl -X PATCH https://api.chronary.ai/v1/webhooks/whk_a1b2c3 \
-H "Authorization: Bearer chr_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"events": ["event.created", "event.updated"],
"active": false
}'

Set active: false to temporarily pause deliveries without deleting the webhook.

Terminal window
curl -X DELETE https://api.chronary.ai/v1/webhooks/whk_a1b2c3 \
-H "Authorization: Bearer chr_sk_your_key_here"

Returns 204 No Content.

  • Always verify signatures — never trust a webhook payload without checking the HMAC
  • Return 200 quickly — process the payload asynchronously if it takes more than a few seconds
  • Handle duplicates — use the event id to deduplicate, as retries may deliver the same event twice