Webhooks
Webhooks push real-time notifications to your server when things happen in Chronary — events created, updated, deleted, or agents modified. No polling required.
How it works
Section titled “How it works”- You register a webhook URL and choose which event types to listen for
- When a matching event occurs, Chronary sends an HTTP POST to your URL
- The request includes an HMAC-SHA256 signature for verification
- Failed deliveries are automatically retried with exponential backoff
Create a webhook
Section titled “Create a webhook”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"] }'chronary webhooks create \ --url "https://your-server.com/webhooks/chronary" \ --events "event.created, event.updated, event.deleted"# Created webhook wh_a1b2c3d4# Secret: whsec_abc123...# (Save this secret — it won't be shown again)const response = await fetch('https://api.chronary.ai/v1/webhooks', { method: 'POST', headers: { 'Authorization': 'Bearer chr_sk_your_key_here', 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://your-server.com/webhooks/chronary', events: ['event.created', 'event.updated', 'event.deleted'], }),});const webhook = await response.json();console.log(webhook.id); // "whk_a1b2c3"console.log(webhook.secret); // Save this — used for HMAC verificationfrom chronary import Chronary
client = Chronary()
webhook = client.webhooks.create( url="https://your-server.com/webhooks/chronary", events=["event.created", "event.updated", "event.deleted"],)print(webhook.secret) # Save this for HMAC verificationEvent types
Section titled “Event types”| 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 |
Webhook payload
Section titled “Webhook payload”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" } }}Verifying signatures
Section titled “Verifying signatures”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');}from chronary.webhook import verify_signature
# In your Flask/FastAPI request handler:is_valid = verify_signature( payload=request.body, signature=request.headers["X-Chronary-Signature"], secret=os.environ["WEBHOOK_SECRET"],)if not is_valid: return Response("Invalid signature", status=401)Retry behavior
Section titled “Retry behavior”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.
List webhooks
Section titled “List webhooks”curl https://api.chronary.ai/v1/webhooks \ -H "Authorization: Bearer chr_sk_your_key_here"Update a webhook
Section titled “Update a webhook”Change the URL, event types, or active status:
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.
Delete a webhook
Section titled “Delete a webhook”curl -X DELETE https://api.chronary.ai/v1/webhooks/whk_a1b2c3 \ -H "Authorization: Bearer chr_sk_your_key_here"Returns 204 No Content.
Best practices
Section titled “Best practices”- 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
idto deduplicate, as retries may deliver the same event twice