Subscribe
Webhook subscriptions are created and managed in the admin UI. Each subscription has a target URL, an event filter (* or an explicit list), and its own signing secret.
Manage webhook subscriptions
Create endpoints, choose events, copy the signing secret. The full
whsec_* secret is shown once on creation — store it somewhere safe.whsec_<56-hex-chars> (62 characters total). Each subscription has its own secret; rotating a secret is a destructive action that invalidates the previous one.
Events
Thirteen events fan out from the BoothZen observer chain. Every event delivers a JSON body with a top-levelid, type, created, and data.object (the resource snapshot at the moment of the event).
The canonical list is the platform’s App\Services\Webhooks\WebhookEventCatalog. The Laravel SDK exposes typed constants — BoothZen\Laravel\Webhooks\Events::BOOKING_CREATED etc. — that mirror this catalog 1:1.
Booking events (4)
Booking events (4)
| Event | Fires when | Payload root |
|---|---|---|
booking.created | A new booking is inserted, by any path (admin, widget, API) | data.object = full Booking |
booking.updated | A booking’s mutable fields change (notes, dates, services) | data.object = full Booking |
booking.confirmed | A booking moves into the confirmed status | data.object = full Booking |
booking.cancelled | A booking is cancelled | data.object = full Booking |
Payment events (2)
Payment events (2)
| Event | Fires when |
|---|---|
payment.received | Payment recorded against an invoice or booking (fires on Stripe’s payment_intent.succeeded) |
payment.refunded | Refund issued (full or partial) |
Quote events (3)
Quote events (3)
| Event | Fires when |
|---|---|
quote.created | A new quote is generated and saved |
quote.accepted | Customer accepts a quote |
quote.declined | Customer declines a quote |
Lead events (1)
Lead events (1)
| Event | Fires when |
|---|---|
lead.created | New lead captured (widget, manual, API, embed) |
Customer events (1)
Customer events (1)
| Event | Fires when |
|---|---|
customer.created | First time a customer record is created |
Invoice events (2)
Invoice events (2)
| Event | Fires when |
|---|---|
invoice.sent | Invoice issued to customer |
invoice.paid | Invoice marked fully paid |
There is no
payment.succeeded or payment.failed event — payment.received is the only success signal. There is no booking.status_changed or booking.completed — use booking.confirmed / booking.cancelled for status transitions. There are no *.updated events for customer, lead, or invoice; only *.created / invoice.sent / invoice.paid fire for those resources. Availability does not emit events — derive from booking events instead.data.object for each resource is part of the OpenAPI contract. Browse it in the API Reference tab — the webhook resource shape matches the corresponding GET /api/v1/{resource}/{id} response.
Signature verification
Every delivery carries aBoothZen-Signature header in Stripe’s signature format:
t— Unix timestamp (seconds) when the signature was computed.v1— HMAC-SHA256 of the signed payload string, encoded as lowercase hex.
., then the raw request body bytes (do NOT parse and re-serialise — verify against the bytes you received).
Tolerance window
Reject deliveries wheret is more than 5 minutes in the past or future (the same default Stripe uses). This stops replay attacks where an attacker re-POSTs an old signed body.
Code
Retries
BoothZen retries failed deliveries (non-2xx response or connection error) with exponential backoff:| Attempt | Delay after previous |
|---|---|
| 1 | immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 12 hours |
2xx as soon as you’ve persisted the event; do the heavy work in a background job.
SDK helper
The official Laravel SDK ships aSignatureVerifier helper so you don’t have to copy-paste the snippets above into every project.
boothzen/laravel-sdk on Packagist
composer require boothzen/laravel-sdk — includes BoothZen\Sdk\Webhooks\SignatureVerifier and a Laravel middleware (boothzen.webhook) that verifies and rejects invalid deliveries before they reach your controller.