Skip to Content
REST APIStripe webhooks

Stripe webhooks

Stripe sends donation, payout, and subscription events to Ministrium via webhook. This page explains how those are processed and how to verify the signature if you choose to consume webhooks from Ministrium to your backend.

Stripe → Ministrium webhooks (managed)

When you connect Stripe (via Stripe Connect), Ministrium automatically creates an endpoint in your Stripe account:

https://api.ministrium.com/v1/webhooks/stripe/<tenant>

Subscribed events: see Stripe Connect → Webhooks.

These webhooks are internal — you don’t configure them. They let Ministrium react to Stripe changes in real time.

Ministrium → your backend webhooks (outbound)

If you want Ministrium to notify your backend when something happens (donation, new member, etc.):

  1. Settings → API → Webhooks → New.
  2. Endpoint URL (HTTPS required).
  3. Events to subscribe to (multiple allowed).
  4. Save → Ministrium generates a signing secret whsec_....

Available events

member.created member.updated member.deleted prospect.created prospect.converted attendance.recorded donation.created donation.refunded donation.disputed recurring.created recurring.updated recurring.canceled recurring.charge_failed event.created event.registration event.canceled group.created group.multiplied group.closed prayer_request.created prayer_request.answered campus.created tag.added tag.removed device.registered

Payload structure

{ "id": "evt_xyz789", "type": "donation.created", "created": 1714165800, "tenant": "la-roca", "api_version": "2026-01-01", "data": { "object": { "id": "don_abc123", "amount": 50000, "currency": "MXN", "fund": { "id": "fnd_tithes", "name": "Tithes" }, "donor": { "id": "mem_456", "first_name": "John" }, "campus": { "id": "cmp_downtown", "name": "Downtown" }, "created_at": "2026-04-26T14:30:00Z" } } }

tenant always present to identify which church it belongs to (useful if you operate for several).

Signature verification

Ministrium signs each webhook with HMAC-SHA256:

Headers: Ministrium-Signature: t=1714165800,v1=abcd1234...

Node.js verification:

import crypto from 'crypto'; function verify(payload, header, secret) { const [tPart, vPart] = header.split(','); const timestamp = tPart.split('=')[1]; const signature = vPart.split('=')[1]; // 5-minute tolerance to prevent replay if (Math.abs(Date.now()/1000 - timestamp) > 300) return false; const expected = crypto .createHmac('sha256', secret) .update(`${timestamp}.${payload}`) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ); }

> Important: use the raw body (not the parsed JSON) to compute the signature.

Retries and idempotency

If your endpoint doesn’t respond 2xx in < 3 s, Ministrium retries with backoff (see Rate limits).

Each event has a unique id. Your backend must be idempotent: store processed IDs and discard duplicates.

if (await db.events.exists({ id: payload.id })) return res.status(200).end(); await db.events.insert({ id: payload.id, ... });

Replay and backfill

In Settings → API → Webhooks → [Endpoint] you can:

  • Resend a single event (useful if your backend had a bug).
  • Backfill a date range (all events of type X between date A and B).
Optional filters

You can filter the send to not receive everything. E.g.: only donation.created with amount > 100000 (≥ $1,000).

Best practices

  1. Always verify the signature. Replay attacks are real.
  2. Respond 200 quickly and process async.
  3. Store the raw event before processing (debugging).
  4. Monitor failures: if your endpoint goes down, webhooks deactivate after 5 retries.
  5. Have a health-check endpoint (GET /webhooks/ping) Ministrium can call to validate before sending.
Last updated on