Skip to main content
Webhooks are not yet available. This page documents the planned design so you can prepare your integration architecture. Event names and payload shapes may change before release. Until webhooks ship, use the polling and revalidation patterns below — they cover the same use cases reliably.
Webhooks will let Opencals notify your application the moment something happens in your store — a booking is created, an order is paid, a customer cancels — so you can drive automations without polling.

Planned events

The first release focuses on the six events that power the most common merchant automations:
EventFires whenWhat merchants automate with it
appointment.createdA new booking is madeCRM sync, welcome and confirmation flows
order.paidPayment is captured for an orderFulfillment, accounting entries
appointment.canceledA booking is canceledWaitlist backfill, freeing resources
appointment.rescheduledA booking is moved to a new slotCalendar updates, staff notifications
appointment.completedA booking’s end time passesReview requests, follow-up sequences
order.refundedA refund is issued on an orderAccounting corrections, inventory release

How delivery will work

A webhook is an HTTP POST from Opencals to an endpoint you host. Each delivery:
  • is sent as JSON to the URL you register,
  • identifies its event type so one endpoint can handle many events,
  • is signed, so you can verify the request really came from Opencals,
  • expects a 2xx response. Non-2xx responses are treated as failures and retried with backoff, so make your handler idempotent — the same event may be delivered more than once.
Respond quickly (acknowledge first, process asynchronously) to avoid timeouts on slow handlers.

Verifying signatures

Deliveries will follow the standard HMAC pattern: a signature header computed over the raw request body with your endpoint’s secret. The verification you’d write today won’t change:
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyWebhookSignature(
  rawBody: string,
  signatureHeader: string,
  secret: string,
): boolean {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader);
  return a.length === b.length && timingSafeEqual(a, b);
}
Verify against the raw, unparsed request body — re-serialized JSON can reorder keys and break the comparison. In Next.js route handlers, use await request.text() before parsing.

Keeping data fresh today

Until webhooks ship, two patterns keep an integration accurate:
  • Poll customer-scoped endpoints on your own schedule. GET /storefront/appointments and GET /storefront/orders return current state, so a periodic sync job covers CRM, accounting, and follow-up automations. Respect the rate limits — a sync every few minutes is well within them.
  • Time-based revalidation for catalog data. Products, staff, and locations change rarely — Next.js ISR (revalidate) or a short CDN cache of a few minutes is usually enough for a headless booking site.
  • Always fetch availability live. Time slots change with every booking, so call the availability endpoints at request time rather than caching them. The booking flow is also self-correcting: POST /storefront/cart/checkout/start re-validates every cart item and reports invalid ones before any payment is taken.
Last modified on June 13, 2026