Webhooks

Webhooks

Webhooks let your systems react to booking events in real-time. When a booking is confirmed or cancelled, Slotflow sends an HTTP POST to your registered URL with the event details.

Supported events

EventWhen it fires
booking.confirmedA new booking is created via POST /v1/bookings
booking.cancelledA booking is cancelled via DELETE /v1/bookings/:id

Registering a webhook

1const res = await fetch("https://api.slotflow.dev/v1/webhooks", {
2 method: "POST",
3 headers: {
4 "Authorization": `Bearer ${process.env.SLOTFLOW_API_KEY}`,
5 "Content-Type": "application/json",
6 },
7 body: JSON.stringify({
8 url: "https://your-agent.com/webhooks/slotflow",
9 events: ["booking.confirmed", "booking.cancelled"],
10 }),
11});
  • Each URL can only be registered once per organization (duplicates are rejected)
  • You can register for one or both events
  • The URL must be reachable from the internet

Webhook limits by plan

PlanMax webhooks
Free2
Starter10
GrowthUnlimited

Payload format

Every webhook delivery is an HTTP POST with a JSON body:

1{
2 "event": "booking.confirmed",
3 "created_at": "2026-03-10T14:05:00.000Z",
4 "data": {
5 "id": "b9e4f2a1-3c5d-4e6f-8a9b-0c1d2e3f4a5b",
6 "human_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
7 "starts_at": "2026-03-10T14:00:00.000Z",
8 "ends_at": "2026-03-10T14:30:00.000Z",
9 "duration_minutes": 30,
10 "attendee_name": "Alex Rivera",
11 "attendee_email": "alex@startup.io",
12 "status": "confirmed",
13 "metadata": {
14 "lead_id": "lead_8294",
15 "conversation_id": "conv_1847",
16 "source": "ai_sdr"
17 }
18 }
19}

The data object contains the full booking, including the metadata your agent passed when creating the booking. This is how you connect webhook events back to your agent’s workflow.

Delivery behavior

  • Method: HTTP POST
  • Content-Type: application/json
  • Timeout: 10 seconds — your endpoint must respond within 10 seconds
  • Success: Any 2xx response code

Retry schedule

If your endpoint fails (non-2xx response or timeout), Slotflow retries:

AttemptTiming
1stImmediate
2nd5 minutes later
3rd60 minutes later

After 3 failed attempts, the delivery is marked as failed. No further retries are attempted.

Building a webhook handler

Express.js example

1const express = require("express");
2const app = express();
3
4app.use(express.json());
5
6app.post("/webhooks/slotflow", (req, res) => {
7 const { event, data } = req.body;
8
9 switch (event) {
10 case "booking.confirmed":
11 console.log(`Booking confirmed: ${data.attendee_name} at ${data.starts_at}`);
12 // Update your CRM, send confirmation email, notify the team, etc.
13 handleBookingConfirmed(data);
14 break;
15
16 case "booking.cancelled":
17 console.log(`Booking cancelled: ${data.id}`);
18 // Update your system, re-queue the lead, etc.
19 handleBookingCancelled(data);
20 break;
21 }
22
23 // Always respond 200 quickly — do heavy processing asynchronously
24 res.sendStatus(200);
25});

Python (Flask) example

1from flask import Flask, request
2
3app = Flask(__name__)
4
5@app.route("/webhooks/slotflow", methods=["POST"])
6def slotflow_webhook():
7 payload = request.get_json()
8 event = payload["event"]
9 data = payload["data"]
10
11 if event == "booking.confirmed":
12 handle_booking_confirmed(data)
13 elif event == "booking.cancelled":
14 handle_booking_cancelled(data)
15
16 return "", 200 # Always respond 200 quickly

Using metadata

Metadata is the bridge between your agent’s workflow and webhook events. Whatever JSON you pass in the booking’s metadata field appears in the webhook payload.

Common metadata patterns:

1// AI Sales Agent
2metadata: {
3 lead_id: "lead_8294",
4 conversation_id: "conv_1847",
5 source: "ai_sdr",
6 qualified_at: "2026-03-10T13:45:00Z",
7}
8
9// Customer Support
10metadata: {
11 ticket_id: "ticket_12345",
12 customer_id: "cust_6789",
13 issue_type: "billing",
14 priority: "high",
15}
16
17// Recruiting
18metadata: {
19 application_id: "app_456",
20 job_id: "job_789",
21 stage: "technical_interview",
22}

Your webhook handler reads data.metadata to route the event to the right system:

1app.post("/webhooks/slotflow", (req, res) => {
2 const { event, data } = req.body;
3
4 if (event === "booking.confirmed") {
5 const { source } = data.metadata;
6
7 if (source === "ai_sdr") {
8 crm.updateLead(data.metadata.lead_id, { status: "demo_booked" });
9 } else if (data.metadata.ticket_id) {
10 supportSystem.updateTicket(data.metadata.ticket_id, { status: "callback_scheduled" });
11 }
12 }
13
14 res.sendStatus(200);
15});

Managing webhooks

List webhooks

$curl https://api.slotflow.dev/v1/webhooks \
> -H "Authorization: Bearer sk_live_your_api_key"

Delete a webhook

$curl -X DELETE https://api.slotflow.dev/v1/webhooks/WEBHOOK_ID \
> -H "Authorization: Bearer sk_live_your_api_key"

Best practices

  1. Respond 200 immediately — do heavy processing asynchronously. Slotflow times out after 10 seconds and will retry, which could cause duplicate processing.

  2. Make handlers idempotent — due to retries, you may receive the same event twice. Use data.id (the booking ID) to deduplicate.

  3. Log webhook payloads — store the raw payload for debugging. If something goes wrong, you’ll want to see exactly what was delivered.

  4. Use HTTPS — your webhook URL should use HTTPS in production to protect the payload in transit.

  5. Monitor delivery — check the Slotflow dashboard for failed deliveries. Common causes: endpoint down, slow response (>10s timeout), non-2xx response codes.