Skip to main content

Webhook endpoints

Register HTTPS URLs to receive signed events for every operational mutation. Secrets are generated server-side and shown exactly once — Stripe semantics.

Endpoints​

MethodPathDescription
GET/v1/webhook_endpointsList
POST/v1/webhook_endpointsCreate (returns the whsec_... secret once)
GET/v1/webhook_endpoints/{id}Retrieve (no secret)
PATCH/v1/webhook_endpoints/{id}Update (url, description, enabled, metadata)
DELETE/v1/webhook_endpoints/{id}Delete

Create an endpoint​

curl -s $API/v1/webhook_endpoints -H "Authorization: Bearer $KEY" \
-H "Idempotency-Key: ops-hook" -H "Content-Type: application/json" \
-d '{"url":"https://ops.example.com/bookingflow/hooks",
"description":"Park ops automation"}'

The response includes "secret": "whsec_..." — store it now; it is never shown again. URLs must be HTTPS.

Verifying signatures​

Each delivery carries:

BookingFlow-Signature: t=1735689600,v1=<hex hmac>

The MAC is HMAC-SHA256 over "{t}.{rawBody}" with the endpoint secret:

const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
// compare with timing-safe equality; reject t older than 5 minutes

Events delivered​

booking.created, booking.updated, booking.confirmed, booking.cancelled, customer.created, customer.updated, customer.deleted, accommodation.created, accommodation.updated, pitch.created, pitch.updated, meter_reading.created, invoice.created, rate.updated, rates.bulk_updated, webhook_endpoint.*.

The payload is an event object; data.object is the resource at event time and update events include previous_attributes. Delivery is at-least-once; treat duplicate deliveries idempotently (e.g. key on event.id). Delivery results are recorded on the event and visible via GET /v1/events.