Webhooks
Instead of polling, let Club-tool push a signed notification to your endpoint when something changes. A club administrator registers endpoints under System → API → Webhooks, picks the events, and gets a signing secret (shown once).
Events
| Event | Fires when |
|---|---|
member.created / member.updated / member.deleted |
A member is created, changed (incl. via bulk) or deleted. |
member.archived | A member is archived (membership ended). |
event.created / event.updated / event.deleted |
A calendar event changes. |
attendance.updated | An attendance record is upserted
(resource_id = the member). |
kennismaker.created / kennismaker.updated |
A trial member signs up or changes (incl. status transitions). |
event.registration | Someone registers for a calendar event
(resource_id = the registration). |
charge.created | A membership-fee charge is created
(resource_id = the charge). |
payment.received | A charge is marked paid — manually or via
the payment provider (resource_id = the charge). |
payment.failed | A direct-debit payment fails or is charged
back and the charge reopens (resource_id = the charge). |
ping | The test button in the app. |
Payload — minimal by design
The body carries ids only, never personal data: fetch details through the API with your own key and scopes. That keeps the GDPR surface of your endpoint small.
{
"id": 812,
"event": "member.updated",
"resource_type": "member",
"resource_id": 41,
"club_id": 12,
"occurred_at": "2026-07-02T14:03:11",
"attempt": 1
}
Verify the signature
Every delivery is signed with HMAC-SHA256 over the raw request body, using your endpoint's secret:
X-Clubtool-Event: member.updated
X-Clubtool-Delivery: 812
X-Clubtool-Signature: sha256=3f1c2e…
# Python example
import hmac, hashlib
def verify(secret: str, body: bytes, header: str) -> bool:
digest = 'sha256=' + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(digest, header)
Reject requests with a missing or invalid signature, and respond within 5 seconds — do heavy work asynchronously after acknowledging.
Delivery, retries & auto-disable
- Respond with any 2xx to acknowledge. Anything else (or a timeout) schedules a retry.
- Retries back off: ~5 min → 30 min → 2 h → 12 h (5 attempts in total). The
attemptfield counts up; deliveries may arrive out of order — useoccurred_atand the API as the source of truth. - After 20 consecutively failed deliveries the endpoint is disabled automatically; re-enable it in the app after fixing your side.
- The app shows a per-endpoint delivery log (last 20, kept 14 days) and a
test button that sends a
ping.
Endpoint requirements
- https only, on a publicly resolvable address (private and loopback ranges are refused).
- Expect at-least-once delivery: make your handler idempotent, e.g. by storing
the delivery
id.