Docs
← chronapilot.com v 2026-05-12

Webhooks

Webhooks deliver events from ChronaPilot to your server. They're how you receive event.created, departure.updated, connection.disconnected, and dozens of other lifecycle signals.

Register an endpoint

curl
curl https://api.chronapilot.com/v1/webhook_endpoints \
  -X POST \
  -H "Authorization: Bearer sk_live_…" \
  -d '{
    "url": "https://your-app.com/webhooks/chronapilot",
    "events": ["event.created", "event.updated", "departure.updated"]
  }'

The response includes a secret — store it. You'll never see it again.

Receiving deliveries

Each delivery is a POST to your URL with:

HTTP
POST /webhooks/chronapilot HTTP/1.1
Chronapilot-Signature: t=1716247200,v1=5257a869e7ecebeda32afb5e26a52e9eda1a8b1...
Chronapilot-Event: whe_8x9y0z1a2b
Content-Type: application/json

{
  "id": "whe_8x9y0z1a2b",
  "type": "event.created",
  "api_version": "2026-05-20",
  "created": 1716247200,
  "data": { "object": { … } },
  "livemode": true
}

Verify the signature

The Chronapilot-Signature header has two comma-separated fields:

  • t — Unix timestamp (seconds) when the signature was generated.
  • v1 — HMAC-SHA256 hex digest.

Compute the expected signature as:

text
signed_payload = t + "." + raw_request_body
expected = HMAC_SHA256(secret, signed_payload).hex()

raw_request_body is the exact bytes you received — do not parse and re-serialize the JSON. Compare expected to v1 using a constant-time comparator. Then reject any delivery where now - t > 300 seconds (5-minute tolerance) to defeat replay attacks.

Node.js
import { Webhook } from '@chronapilot/node';

app.post('/webhooks/chronapilot',
  express.raw({type: 'application/json'}),
  (req, res) => {
    try {
      const event = Webhook.constructEvent(
        req.body,
        req.headers['chronapilot-signature'],
        process.env.WEBHOOK_SECRET
      );
      // handle event.type
      res.json({ received: true });
    } catch (err) {
      res.status(400).send('Invalid signature');
    }
  }
);

Always use the raw request body (bytes), not the parsed JSON. Re-serialized JSON will not produce a matching signature.

Retries

Failed deliveries (non-2xx, or no response within 8s) are retried up to 24 times over 72 hours with exponential backoff. After that the endpoint is marked unhealthy and the delivery is logged but not retried.

You can disable an endpoint temporarily without deleting it; queued deliveries pause until you re-enable.

Ordering

ChronaPilot does not guarantee delivery order. Two events fired close together may arrive at your server in either order. Always handle events by their created timestamp, and treat each delivery as the latest state of that resource.

Replay

Every delivery is logged in the dashboard for 30 days. From the dashboard, you can:

  • Inspect the request body and your response.
  • Re-fire any past delivery to your endpoint or a different URL.
  • Bulk-replay a window of deliveries (useful after a downstream outage).

Catalog

See the webhook event catalog for every event type and payload shape.