Webhooks
Outbound webhook event catalog, signatures, and delivery semantics.
Organizers can register HTTPS webhook endpoints (with a signing secret
and an event-type subscription list) to receive Mosaic events. Delivery
is at-least-once: consume idempotently, keyed by the
x-mosaic-delivery header.
Event catalog
| Event | Fired when | Payload data |
|---|---|---|
order.paid | An order settles: Stripe payment succeeded, bank transfer marked paid, or a free/complimentary order issued | orderId, orderRef, eventId, totals |
order.cancelled | An order leaves the live state: fully refunded (reason: "refund") or unpaid expiry (reason: "expired") | orderId, orderRef, eventId, reason |
ticket.claimed | A holder claims their ticket | ticketId, orderId, eventId, offerId |
refund.succeeded | Refund money settled at Stripe (including refunds initiated in the Stripe dashboard, reconciled by Mosaic). Manual bank-transfer refunds do not fire this event — they are settled out of band. | refundId, orderId, orderRef, eventId, amount |
The request body is a JSON envelope:
{ "type": "order.paid", "at": 1750000000000, "data": { "...": "..." } }Signatures
Every delivery is signed with the endpoint's secret:
x-mosaic-event: order.paid
x-mosaic-delivery: <delivery id>
x-mosaic-signature: t=<epoch ms>,v1=<hex hmac-sha256>v1 is the HMAC-SHA256 of `${t}.${body}` with your endpoint
secret — the same scheme Stripe uses, so existing verification code
transfers directly. Reject stale timestamps (e.g. older than 5 minutes)
to prevent replays.
Delivery and retries
- A
2xxresponse marks the deliverydelivered. Anything else — including redirects, which are never followed — counts as a failed attempt. - Failed attempts retry with exponential backoff (1 min doubling up to
6 h). After 8 attempts the delivery is dead-lettered
(
state: "dead") and will not retry. - Requests time out after 10 seconds.
Endpoint requirements
- HTTPS only, addressed by a public DNS name — IP literals, localhost, internal domains, and single-label hostnames are rejected at registration AND at dispatch (the delivery worker re-validates the resolved addresses against private/reserved ranges before every attempt).
- Endpoints can be disabled without being deleted; disabled endpoints receive nothing.