Webhooks β
Inbound β that we receive β
Genius Checkout β
URL: POST /functions/v1/gc-webhook
GC posts events for every subscription state change (created, charged, paused, cancelled, payment_failed). Each post carries:
X-GC-Signatureβ HMAC-SHA256 over the raw body using your org'swebhook_secret.Idempotency-Keyβ GC retries unsigned 5xx responses for ~24 hours, so guard against duplicates on your side.
We verify the signature, look up the matching recurring_donations row by gateway_subscription_id, and update its state. The full payload is logged to donation_payment_webhook_log so you can replay if a write fails.
WAHA / WhatsApp inbound replies β
URL: POST /functions/v1/wacrm-webhook
WAHA posts JSON for every inbound WhatsApp message + delivery receipt. We normalize the phone number (digits-only, no +), match it against members.primary_phone, and append the message to the existing conversation thread (or create a new one).
PowerTranz refunds + notifications β
URL: POST /functions/v1/powertranz-webhook
Used for asynchronous refund confirmations + recurring-payment notifications. Same HMAC pattern as GC.
Outbound β that you can configure β
Per-event webhooks β
Go to Settings β Integrations β Webhooks and add a URL. Pick from:
| Event | Fires when |
|---|---|
member.created | A new member is added (manually, bulk, or via visitor form) |
member.lost | A member is marked lost |
donation.recorded | A donation row is inserted (manual or online) |
attendance.marked | An attendance row is inserted |
workflow.completed | A workflow run finishes |
Each post includes:
X-GCM-Signatureβ HMAC-SHA256 of the body using a secret you set.- A JSON body with the affected row + delta.
Zapier β
We ship a Zapier integration (search "Genius Church Manager" in Zapier). Use it for low-code automations. Under the hood it's the same outbound webhook endpoint.
Verifying signatures β
Pseudocode (the same algorithm works in Node, Python, PHP, Go):
expected = HMAC_SHA256(secret, raw_body).hex()
got = request_header('X-GCM-Signature')
if not constant_time_compare(expected, got):
return 401WARNING
Don't use == β timing-safe comparison only. Stripe and Slack both publish more detailed guides, both apply here.
Retries β
We retry failed posts (status β 2xx, or no response in 10s) with exponential backoff up to 24 hours, then mark the delivery as permanently_failed. Check delivery history under Settings β Integrations β Webhook Logs.