Webhooks & Integration Events
Memvera pushes signed JSON events to your systems (ERP, communication platforms, BI) as they happen — no polling. A gym admin configures endpoints under Settings → Webhooks in the staff portal: the destination URL, which events to receive, optional auth headers (e.g. your API key), and a signing secret.
Event catalog
| Domain | Events |
|---|---|
| Memberships | membership.contract.activated (sold) · membership.contract.renewed · membership.contract.cancelled · .frozen / .unfrozen · .expired · .rewritten · .blocked / .unblocked · .plan_changed · .drafted · membership.reactivated · membership.renewal.failed · membership.grace.expired · membership.agreement.generated · cancellation requested/withdrawn |
| Scheduling | scheduling.booking.created · scheduling.booking.cancelled · scheduling.booking.attended · scheduling.booking.no_show · scheduling.session.conducted |
| Finance | finance.payment.settled · finance.refund.issued · finance.retail.sale.completed · finance.cash.variance_flagged |
| People & access | identity.member.created (members and leads — see profile_type) · facility.member.checked_in |
| Fitness | workouts.activity.recorded |
Subscribe to specific events or * for all.
Example payloads
scheduling.booking.created:
{
"booking_id": "01j8...", "brand_id": "01hz...", "member_id": "01j2...",
"session_id": "01j8...", "session_type": "gx", "class_name": "HIIT 45",
"starts_at": "2026-08-05T18:00:00+03:00", "club_id": "01hz...", "status": "booked"
}scheduling.booking.cancelled — reason is one of member, staff, freeze, session_cancelled:
{
"booking_id": "01j8...", "brand_id": "01hz...", "member_id": "01j2...",
"session_id": "01j8...", "starts_at": "2026-08-05T18:00:00+03:00",
"reason": "freeze", "previous_status": "booked"
}membership.contract.renewed:
{
"contract_id": "01j9...", "brand_id": "01hz...", "member_id": "01j2...",
"plan_id": "01hz...", "amount_minor": 150000, "currency": "SAR",
"activation_date": "2026-08-04T10:30:00+03:00", "duration_months": 12
}Delivery contract
Every delivery is an HTTP POST with these headers:
| Header | Meaning |
|---|---|
X-Memvera-Event | The event name (scheduling.booking.created, …) |
X-Memvera-Delivery | Unique delivery ID — dedupe on this; retries reuse it |
X-Memvera-Signature | HMAC-SHA256(body, your signing secret), hex |
| (your custom headers) | Whatever the admin configured (API keys etc.) |
Respond with any 2xx within 15 seconds. Non-2xx or timeouts are retried up to 5 times with linear backoff (1, 2, 3, 4 minutes), then dead-lettered — visible (and re-deliverable) in Settings → Webhooks → Deliveries.
Verifying the signature
Node.js:
import crypto from "node:crypto";
function verify(rawBody, signatureHeader, secret) {
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}PHP:
$expected = hash_hmac('sha256', $rawBody, $secret);
$valid = hash_equals($expected, $request->header('X-Memvera-Signature'));Compute the HMAC over the raw request body
Parse the JSON only after verifying — re-serialising can reorder keys and break the signature.
Testing
Use the Send test button next to your subscription — it fires a test.ping event and shows your endpoint's response code. webhook.site is handy while developing.