Skip to content

Outbound webhooks ​

When Zapier isn't enough β€” you want to write your own backend, run your own automations, pipe events into a data warehouse, or trigger services Zapier doesn't support β€” outbound webhooks give you the raw firehose.

A webhook is just an HTTPS endpoint you host. GCM POSTs JSON to it the moment something happens in your org. Same delivery infrastructure as the Zapier integration, but pointing at your URL instead of Zapier's.

Webhook subscriptions

When to use webhooks vs Zapier ​

  • Use Zapier when the destination is one of the 6,000+ apps Zapier already supports, you don't want to host code, and you're fine with Zapier's per-task pricing.
  • Use webhooks when you have your own backend, you want a single payload pushed to your warehouse / queue / Lambda, or you want zero per-event cost beyond your own hosting.

Both share the same retry policy, signature scheme, and delivery log. The only difference is which URL receives the POST.

Subscribing to events ​

There are two ways to create a webhook subscription:

Through the API ​

POST to /v1/subscriptions with your API key:

bash
curl -X POST https://api.geniuschurchmanager.com/v1/subscriptions \
  -H "Authorization: Bearer gcm_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "member.created",
    "target_url": "https://your-server.example.com/gcm/webhook"
  }'

The response contains a subscription_id. Save it β€” you'll need it to deactivate later.

Through the UI ​

In Integrations β†’ Zapier, the Subscriptions tab shows every active subscription, regardless of whether Zapier or a hand-rolled script created it. You can disable but not create subscriptions from the UI β€” that's intentional, since target URLs should be code-managed.

Available events ​

Same catalog as Zapier:

EventTrigger
member.createdAny new member row
member.updatedTracked field changes on a member
donation.createdDonation row inserted
donation.refundedRefund processed
attendance.recordedCheck-in or attendance saved
workflow.completedWorkflow run finishes
form.submittedPublic form submitted
group.member_addedMember added to a group / ministry

See the webhooks API reference for the exact field shape of each event.

Signature verification ​

Every POST carries an X-GCM-Signature header β€” an HMAC-SHA256 hex digest over the raw request body, keyed with your subscription's secret.

js
import crypto from "node:crypto";

function verify(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

WARNING

Always verify the signature before parsing the body or doing anything with the payload. Unsigned or invalid POSTs to your endpoint could be anyone.

The signing secret is shown once when the subscription is created. Store it in your secret manager.

Response contract ​

Your endpoint should:

  • Return HTTP 2xx within 10 seconds β€” we treat anything else as a failure.
  • Be idempotent β€” we may retry, and the same event ID may arrive twice. The payload includes a delivery_id you can use to dedupe.
  • Do work async if it's slow. Persist the payload to a queue and return 200 immediately; process at your leisure.

A 4xx response is treated as "this request was malformed and won't succeed on retry." We don't retry. A 5xx is treated as "the endpoint is having a bad day" and we retry with backoff.

Retry policy ​

Failed deliveries are retried with exponential backoff:

AttemptDelay after previous failure
1initial
21 minute
35 minutes
425 minutes
52 hours

After 5 total failures, the delivery row is marked failed and we stop. After 10 consecutive failures across deliveries, the subscription itself is auto-disabled β€” you'll see a red badge in the UI and we'll email the org admin.

Inspecting deliveries ​

The Recent deliveries tab in Integrations β†’ Zapier shows the last 50 attempts across every subscription (Zapier and webhooks alike).

Webhook delivery log

Each row has the HTTP status, retry count, response excerpt, and timestamp. When troubleshooting "why didn't event X fire," this is the first place to look.

Inbound webhooks (the reverse direction) ​

You can also have your system POST into GCM to trigger workflows. Configure an inbound webhook trigger in any workflow:

  1. Build a workflow with a Webhook trigger node.
  2. GCM gives you a unique slug URL (/functions/v1/receive-webhook/{slug}).
  3. Set up your external system to POST to that URL with X-Webhook-Signature (HMAC-SHA256 over the body using the secret GCM shows you).
  4. Every valid POST kicks off a new workflow run with the payload available to subsequent steps.

This is the path for receiving events from systems that don't have a Zapier integration β€” your accounting software, your live-streaming platform, a custom registration site.

Rate limits ​

LimitDefault
Active subscriptions per org100
Delivery rate per subscription60 / minute
Payload size256 KB
Inbound webhook rate60 / minute / slug

Cross-references ​