Webhooks

Receive real-time HTTP notifications whenever events occur in SeroBook. Webhooks let your application react instantly to new bookings, cancellations, customer updates, and payments without polling the API.

Pro plan and above

Webhooks are available on Pro plans and above. Upgrade from Settings → Billing to enable webhook support.


What Are Webhooks?

A webhook is an HTTP callback that SeroBook sends to a URL you specify whenever a specific event occurs. Instead of your application repeatedly asking SeroBook "has anything changed?" (polling), SeroBook proactively tells your application the moment something happens.

This approach is more efficient, more timely, and reduces unnecessary API calls. Webhooks are essential for building responsive integrations that need to react immediately to booking events.

When an event occurs, SeroBook sends an HTTP POST request to your endpoint with a JSON payload containing the event type and the relevant data. Your endpoint should respond with a 200 status code to acknowledge receipt.


Setting Up Webhook Endpoints

Configure your webhook endpoints from the SeroBook dashboard:

  1. Navigate to Settings → Integrations → Webhooks
  2. Click Add Endpoint
  3. Enter your Endpoint URL (must be HTTPS)
  4. Select the events you want to subscribe to
  5. Optionally, add a description for easy identification
  6. Click Save to activate the endpoint
HTTPS required

Webhook endpoints must use HTTPS. SeroBook will not deliver events to HTTP URLs. If you're developing locally, use a tool like ngrok or localtunnel to create a secure tunnel to your local server.

Endpoint Requirements

  • Must accept POST requests
  • Must respond with a 2xx status code within 30 seconds
  • Must be publicly accessible (not behind a firewall or VPN without proper configuration)
  • Should process the payload asynchronously if the handling takes significant time

Available Events

Subscribe to one or more of the following events. Each event fires when the corresponding action occurs in SeroBook.

Booking Events

  • booking.created — A new booking has been created (from the dashboard, booking page, or API)
  • booking.updated — A booking has been modified (date, time, service, status, or notes changed)
  • booking.cancelled — A booking has been cancelled (by the customer, staff, or via API)

Customer Events

  • customer.created — A new customer record has been created

Payment Events

  • payment.received — A payment has been successfully processed for a booking
Event filtering

Subscribe only to the events you need. This reduces unnecessary traffic to your endpoint and simplifies your handling logic. You can update your subscriptions at any time from the webhook settings.


Payload Format

Every webhook delivery includes a JSON payload with a consistent structure. The top-level fields identify the event, and the data object contains the resource details.

booking.created Example

{
  "id": "evt_abc123",
  "type": "booking.created",
  "created_at": "2024-03-15T10:30:00Z",
  "data": {
    "id": "bk_def456",
    "customer": {
      "id": "cus_ghi789",
      "name": "Jane Smith",
      "email": "jane@example.com",
      "phone": "+1-555-0123"
    },
    "service": {
      "id": "svc_jkl012",
      "name": "Consultation",
      "duration": 60
    },
    "date": "2024-03-20",
    "time": "14:00",
    "status": "confirmed",
    "price": 75.00,
    "notes": "First-time customer",
    "created_at": "2024-03-15T10:30:00Z"
  }
}

booking.cancelled Example

{
  "id": "evt_mno345",
  "type": "booking.cancelled",
  "created_at": "2024-03-16T09:15:00Z",
  "data": {
    "id": "bk_def456",
    "customer": {
      "id": "cus_ghi789",
      "name": "Jane Smith",
      "email": "jane@example.com"
    },
    "service": {
      "id": "svc_jkl012",
      "name": "Consultation"
    },
    "date": "2024-03-20",
    "time": "14:00",
    "status": "cancelled",
    "cancellation_reason": "Schedule conflict",
    "cancelled_at": "2024-03-16T09:15:00Z",
    "cancelled_by": "customer"
  }
}

payment.received Example

{
  "id": "evt_pqr678",
  "type": "payment.received",
  "created_at": "2024-03-15T10:35:00Z",
  "data": {
    "id": "pay_stu901",
    "booking_id": "bk_def456",
    "customer_id": "cus_ghi789",
    "amount": 75.00,
    "currency": "USD",
    "method": "card",
    "status": "succeeded",
    "processed_at": "2024-03-15T10:35:00Z"
  }
}

Webhook Headers

Every webhook delivery includes the following HTTP headers:

Content-Type: application/json
X-SeroBook-Event: booking.created
X-SeroBook-Delivery: dlv_abc123
X-SeroBook-Signature: sha256=a1b2c3d4e5f6...
X-SeroBook-Timestamp: 1710500400
  • X-SeroBook-Event — The event type that triggered the delivery
  • X-SeroBook-Delivery — A unique ID for this specific delivery attempt
  • X-SeroBook-Signature — HMAC signature for verifying authenticity
  • X-SeroBook-Timestamp — Unix timestamp of when the event was sent

Security & Signature Verification

To ensure that webhook deliveries are genuinely from SeroBook (and not a malicious third party), you should verify the signature included in every request. SeroBook signs each delivery using HMAC-SHA256 with your webhook signing secret.

Getting Your Signing Secret

  1. Go to Settings → Integrations → Webhooks
  2. Click on your endpoint to view its details
  3. Copy the Signing Secret (starts with whsec_)
  4. Store it securely in your application's environment variables

Verifying the Signature (Node.js)

import crypto from "crypto";

function verifyWebhookSignature(payload, signature, timestamp, secret) {
  const signedContent = timestamp + "." + payload;
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(signedContent)
    .digest("hex");

  const expected = "sha256=" + expectedSignature;

  // 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/serobook", (req, res) => {
  const signature = req.headers["x-serobook-signature"];
  const timestamp = req.headers["x-serobook-timestamp"];
  const payload = JSON.stringify(req.body);

  const isValid = verifyWebhookSignature(
    payload,
    signature,
    timestamp,
    process.env.SEROBOOK_WEBHOOK_SECRET
  );

  if (!isValid) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  // Process the event
  const event = req.body;
  console.log("Received event:", event.type);

  // Always respond quickly
  res.status(200).json({ received: true });
});
Always verify signatures

Never process webhook payloads without verifying the signature first. Without verification, an attacker could send fake events to your endpoint. Additionally, check that the timestamp is within 5 minutes of the current time to prevent replay attacks.


Retry Policy

If your endpoint fails to respond with a 2xx status code within 30 seconds, SeroBook will retry the delivery using an exponential backoff schedule:

  • 1st retry: 1 minute after the initial attempt
  • 2nd retry: 5 minutes after the 1st retry
  • 3rd retry: 30 minutes after the 2nd retry
  • 4th retry: 2 hours after the 3rd retry
  • 5th retry: 8 hours after the 4th retry

After 5 failed attempts, the delivery is marked as failed and no further retries will be made. You can manually retry failed deliveries from the webhook logs in your dashboard.

Idempotency

Your webhook handler may receive the same event more than once due to retries. Design your handler to be idempotent — processing the same event twice should produce the same result. Use the X-SeroBook-Delivery ID to detect and skip duplicate deliveries.

Automatic Disabling

If an endpoint consistently fails (more than 95% failure rate over 7 days), SeroBook will automatically disable it and send you an email notification. You can re-enable the endpoint from the webhook settings once the issue is resolved.


Testing Webhooks

Test your webhook endpoint before relying on it in production:

Using the Dashboard Test Feature

  1. Go to Settings → Integrations → Webhooks
  2. Click on your endpoint
  3. Click Send Test Event
  4. Select the event type to test
  5. SeroBook will send a test payload with sample data
  6. Check the Recent Deliveries log to see the response

Local Development

For local development, use a tunneling service to expose your local server:

# Using ngrok
ngrok http 3000

# Your endpoint URL will be something like:
# https://abc123.ngrok.io/webhooks/serobook

# Using localtunnel
npx localtunnel --port 3000

Copy the generated HTTPS URL and use it as your webhook endpoint in SeroBook. Remember that tunnel URLs are temporary — update the endpoint URL when the tunnel restarts.

Inspecting Deliveries

The webhook dashboard shows a log of all recent deliveries, including:

  • Event type and timestamp
  • HTTP status code returned by your endpoint
  • Request payload that was sent
  • Response body from your endpoint
  • Retry history (if any attempts failed)

Troubleshooting

Common webhook issues and solutions:

Events not being received

  • Verify your endpoint URL is correct and publicly accessible
  • Check that the endpoint is enabled in the webhook settings
  • Ensure you've subscribed to the correct event types
  • Confirm your endpoint accepts POST requests
  • Check your server logs for incoming requests

Signature verification failing

  • Make sure you're using the correct signing secret for this specific endpoint
  • Verify you're computing the HMAC over the raw request body (not a parsed and re-serialized version)
  • Check that the timestamp is included in the signature computation
  • Ensure you haven't accidentally regenerated the signing secret

Endpoint returning errors

  • Check your server logs for exceptions or stack traces
  • Verify your endpoint can parse the JSON payload correctly
  • Ensure your handler responds within the 30-second timeout
  • Move heavy processing to a background job and respond with 200 immediately

Receiving duplicate events

  • This is normal behavior due to the retry mechanism
  • Use the X-SeroBook-Delivery header to deduplicate events
  • Store processed delivery IDs in a cache or database and skip duplicates
Quick response pattern

Respond to webhooks with 200 OK as quickly as possible, then process the event asynchronously in a background job. This prevents timeouts and ensures reliable delivery even when your processing logic takes time.


Next Steps

Now that you understand webhooks: