Webhooks
When something happens in a club that has granted your application the events.read scope, Lobo One can tell you — by POSTing to an address you register, signed, retried, and kept in a log you can read.
Subscribing
A subscription belongs to one application and names one club. The club must already have granted the application events.read; a subscription cannot reach a club that has not consented, and it stops delivering the moment the grant is withdrawn — checked when each event is fanned out, not when the subscription was written.
POST /v1/developer-organizations/{developerOrganizationId}/applications/{applicationId}/webhook-subscriptions
{
"tenantId": "…",
"url": "https://hooks.example.com/lobo-one",
"eventNames": ["members.member_created", "bookings.booking_confirmed"],
"description": "CRM sync"
}The response carries the signing secret once. It is stored nowhere you can read it again; rotate it if it is lost. An address must be https, must name a host on the public internet, and must not be an IP address — and at delivery time the name is resolved and refused if it points anywhere private.
What you receive
POST https://hooks.example.com/lobo-one
Content-Type: application/json
lobo-one-event: members.member_created
lobo-one-event-id: 01a0…
lobo-one-delivery-id: 01a0…
lobo-one-timestamp: 1757851200
lobo-one-signature: t=1757851200,v1=5f2c…
{
"id": "01a0…",
"type": "members.member_created",
"version": 1,
"occurredAt": "2026-09-14T12:00:00.000Z",
"tenantId": "…",
"data": { "memberId": "…", "tenantId": "…" }
}data is the payload the owning context published: identifiers and the few facts the event is about, versioned by version. Fetch what you need through the API with your grant; a webhook is a notice that something changed, not a copy of it.
Verifying
The signature is HMAC-SHA256(secret, `${t}.${rawBody}`), hex. Compute it over the raw body — a body re-serialised by your framework is different bytes — compare in constant time, and refuse a timestamp older than a few minutes. For a day after you rotate the secret, deliveries carry a v1= under each secret, so a fleet can switch over without missing anything. @lobo-one/sdk/webhooks does all of this in one call.
Delivery, retries and replay
- A
2xxis delivered. A5xx, a408, a429, a timeout or a connection failure is retried with growing backoff — 30 seconds, a minute, two, four — up to eight times. - Any other
4xxis not retried: a404will be a404in ten minutes. The delivery is set aside as dead. - Redirects are not followed. A redirect is a new address nobody checked.
- A dead delivery can be replayed: the same bytes, to the current address, signed with the current secret. Every attempt — status, timing, reason — is in the log.
- A subscription whose deliveries keep dying is disabled automatically, with the reason kept. Pausing is different: paused deliveries queue and drain when you resume.
- The same event is delivered at most once per subscription. If you see the same
lobo-one-event-idtwice, it was a replay somebody asked for.
Every event you can subscribe to
| Event | What happened | Always in the payload |
|---|---|---|
members.member_created | Somebody was registered as a member of the club. |
|
members.member_status_changed | A member became active, suspended or left. |
|
membership.membership_activated | A membership started. |
|
membership.membership_suspended | A membership was suspended. |
|
membership.membership_reinstated | A suspended membership was reinstated. |
|
membership.membership_terminated | A membership ended. |
|
subscriptions.subscription_activated | A subscription started. |
|
subscriptions.subscription_renewed | A subscription renewed for another period. |
|
subscriptions.subscription_cancelled | A subscription was cancelled. |
|
subscriptions.subscription_expired | A subscription reached its end without renewing. |
|
bookings.booking_confirmed | A booking was confirmed. |
|
bookings.booking_cancelled | A booking was cancelled. |
|
bookings.booking_checked_in | The member arrived for a booking. |
|
bookings.booking_no_show | A booking passed without the member arriving. |
|
services.service_booked | A bookable service was booked. |
|
services.service_cancelled | A booked service was cancelled. |
|
commerce.order_confirmed | An order was confirmed. |
|
commerce.order_cancelled | An order was cancelled. |
|
billing.receivable_issued | Something became owed. |
|
billing.receivable_settled | Something owed was paid. |
|
payments.payment_failed | A payment attempt failed. |
|
wallet.wallet_credited | A member’s wallet was credited. |
|
Nothing outside this catalogue is delivered to anybody. The platform’s own administrative events — a credential issued, a support session opened — are not on it and cannot be subscribed to.