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_test_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/chronary",
"events": ["event.created", "event.updated", "event.deleted"]
}'
Event typeTrigger
event.createdA new event is added to any calendar
event.updatedAn event’s fields are modified
event.deletedAn event is deleted
agent.createdA new agent is created
agent.updatedAn 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:

AttemptDelay
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_test_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_test_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_test_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
  • Use test mode — test keys trigger webhooks with the same behavior as live mode