Webhooks
Receive real-time HTTP notifications when booking events occur.
Webhooks let you receive real-time HTTP POST notifications when events occur in your AstroCal account. Instead of polling the API, register a webhook endpoint and AstroCal will push events to your server as they happen.
Event Types
| Event | Trigger |
|---|---|
booking.created | A new booking is confirmed |
booking.cancelled | An existing booking is cancelled |
booking.rescheduled | An existing booking is rescheduled |
Creating a Webhook Endpoint
Register a URL to receive events:
curl -X POST https://api.astrocal.dev/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/astrocal",
"events": ["booking.created", "booking.cancelled"]
}'The response includes a secret field — save this immediately. The secret is only shown once at creation time and is used to verify webhook signatures.
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://your-app.com/webhooks/astrocal",
"events": ["booking.created", "booking.cancelled"],
"secret": "whsec_abc123...",
"active": true,
"created_at": "2026-03-01T10:00:00.000Z",
"updated_at": "2026-03-01T10:00:00.000Z"
}Payload Format
Each webhook delivery sends a JSON payload with this structure:
{
"event": "booking.created",
"data": {
"id": "770e8400-e29b-41d4-a716-446655440000",
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"event_type_id": "660e8400-e29b-41d4-a716-446655440000",
"status": "confirmed",
"start_time": "2026-03-15T14:00:00.000Z",
"end_time": "2026-03-15T14:30:00.000Z",
"invitee_name": "Jane Smith",
"invitee_email": "jane@example.com",
"invitee_timezone": "America/New_York",
"notes": null,
"created_at": "2026-03-01T10:00:00.000Z",
"updated_at": "2026-03-01T10:00:00.000Z"
},
"created_at": "2026-03-01T10:00:00.123Z"
}Verifying Signatures
Every webhook delivery includes an X-AstroCal-Signature header containing an HMAC-SHA256 signature of the request body. Always verify this signature to ensure the webhook came from AstroCal and wasn't tampered with.
The header format is v1=<hex-digest>.
Node.js / TypeScript Example
import crypto from "node:crypto";
function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean {
const expectedSig = crypto.createHmac("sha256", secret).update(payload).digest("hex");
const expected = `v1=${expectedSig}`;
// Use timing-safe comparison to prevent timing attacks
if (signature.length !== expected.length) return false;
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
// In your webhook handler:
app.post("/webhooks/astrocal", (req, res) => {
const signature = req.headers["x-astrocal-signature"];
const rawBody = req.body; // Must be the raw string, not parsed JSON
if (!verifyWebhookSignature(rawBody, signature, WEBHOOK_SECRET)) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(rawBody);
// Handle the event...
res.status(200).send("OK");
});Headers
Each webhook delivery includes these headers:
| Header | Description |
|---|---|
Content-Type | Always application/json |
X-AstroCal-Signature | HMAC-SHA256 signature (v1=<hex>) |
X-AstroCal-Event | The event type (e.g. booking.created) |
Retry Behavior
If your endpoint returns a non-2xx status code or the request times out (10 seconds), AstroCal will retry with exponential backoff:
| Attempt | Delay After |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 24 hours |
After 6 failed attempts, the delivery is marked as permanently failed. You can view delivery history via the API.
Managing Webhooks
You can manage webhooks from the dashboard or via the API.
Dashboard
The Webhooks page in the developer dashboard lets you:
- Create, edit, and delete webhook endpoints
- Toggle endpoints active/inactive
- View delivery logs with status, HTTP response codes, and retry counts
- Copy the signing secret on creation
Navigate to Dashboard > Webhooks to get started.
List Endpoints
curl https://api.astrocal.dev/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY"Update an Endpoint
curl -X PATCH https://api.astrocal.dev/v1/webhooks/WEBHOOK_ID \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"active": false}'Delete an Endpoint
curl -X DELETE https://api.astrocal.dev/v1/webhooks/WEBHOOK_ID \
-H "Authorization: Bearer YOUR_API_KEY"View Delivery History
curl "https://api.astrocal.dev/v1/webhooks/WEBHOOK_ID/deliveries?status=failed" \
-H "Authorization: Bearer YOUR_API_KEY"Best Practices
- Always verify signatures. Never trust webhook payloads without signature verification.
- Respond quickly. Return a 2xx response within 10 seconds. Process the event asynchronously if needed.
- Handle duplicates. In rare cases, the same event may be delivered more than once. Use the event
data.idfor idempotency. - Use HTTPS. Always use HTTPS URLs for your webhook endpoints in production.
- Monitor deliveries. Check the dashboard or the deliveries endpoint for failed deliveries.