Lumipat sa pangunahing nilalaman

Webhooks

Ang mga Webhooks ay nagpapahintulot sa isang church na magpadala ng real-time notifications sa third-party tools — automation platforms (Zapier, Make, n8n), CRMs, accounting systems, o anumang tumatanggap ng HTTP POST. Kapag ang isang tao, grupo, o household ay nagbabago sa B1, ang B1 ay nagpapadala ng isang signed JSON payload sa bawat URL na naka-subscribe sa event na iyon.

Bago Kang Magsimula

  • Isang church admin na may Edit Church Settings permission ay nag-register at namamahala ng mga webhooks
  • Ang iyong receiving endpoint ay dapat na accessible sa HTTPS sa isang public address
  • Magkaroon ng isang paraan upang secure na mag-store ang signing secret — ito ay ipinapakita lamang nang minsan

Overview

Ang mga Webhooks ay outbound lamang: B1 ay tumatawag sa iyong endpoint, hindi mo tinawag ang B1. Bawat webhook ay isang per-church subscription na binubuo ng isang destination URL, isang signing secret, at isang listahan ng subscribed events.

Ang paghahatid ay gumagamit ng isang durable outbox: kapag ang isang subscribed event ay nangyayari, B1 ay nag-record ng isang delivery row at isang background worker ay POSTs ito sa loob ng humigit-kumulang na isang minuto. Ang mga nabigong deliveries ay muling sinusubok na may exponential backoff. Walang nawala kung ang isang delivery ay mabagal o ang iyong endpoint ay pansamantalang pababa.

Pag-register ng Webhook

Sa B1Admin

Pumunta sa Settings → Developer → Webhooks → New Webhook. Magpasok ng isang pangalan, ang payload URL, at pumili ng mga event na mag-subscribe. Sa save, ang signing secret ay ipinapakita nang minsan — kopyahin ito kaagad at ilagay ito sa iyong integration. Hindi ito kailanman ipapakita muli (maaari mong i-rotate ito mamaya, ngunit hindi mo makukuha ang orihinal).

Sa pamamagitan ng API

Ang lahat ng endpoints ay nasa ilalim ng Membership module base path /membership/webhooks at nangangailangan ng alinman sa isang JWT mula sa isang church admin na may Settings / Edit permission, o isang API key na mintado na may settings:write scope. Ang parehong mga route ay tumatanggap sa pareho. Ito ang nagbibigay-daan sa Zapier at Make na mag-register ng mga webhooks sa ngalan ng church kapag ang isang Zap o scenario ay inilabas.

POST /membership/webhooks
Authorization: Bearer <jwt>
Content-Type: application/json

{
"name": "Zapier — new members",
"url": "https://hooks.zapier.com/hooks/catch/123/abc",
"events": ["person.created", "person.updated", "group.member.added"]
}

Ang create response — at lamang ang create response — ay may kasamang secret:

{
"id": "a1b2c3d4e5f",
"name": "Zapier — new members",
"url": "https://hooks.zapier.com/hooks/catch/123/abc",
"events": ["person.created", "person.updated", "group.member.added"],
"active": true,
"secret": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822c"
}
Method & PathPurpose
GET /membership/webhooksI-list ang mga webhook ng church (secret na inalis)
GET /membership/webhooks/eventsAng catalog ng valid event names
GET /membership/webhooks/:idI-load ang isang webhook
POST /membership/webhooksLumikha (walang id) o i-update (na may id)
POST /membership/webhooks/:id/regenerate-secretI-rotate ang signing secret; ibabalik ang bagong halaga nang minsan
DELETE /membership/webhooks/:idTanggalin ang webhook
GET /membership/webhooks/:id/deliveriesMga kamakailang paghahatid na subok para sa webhook
GET /membership/webhooks/deliveries/:deliveryIdBuong payload at response para sa isang paghahatid
POST /membership/webhooks/deliveries/:deliveryId/redeliverMuling i-queue ang isang paghahatid

Event Catalog

Ang mga event names ay sumusunod sa pattern {entity}.{action}. I-fetch ang live list mula sa GET /membership/webhooks/events.

EventFires when
person.createdIsang tao ay idinagdag
person.updatedAng tao record ay binago
person.destroyedIsang tao ay natanggal
household.createdIsang household ay idinagdag
household.updatedAng household ay nabago
household.destroyedIsang household ay natanggal
group.createdIsang grupo ay idinagdag
group.updatedAng grupo ay nabago
group.destroyedIsang grupo ay natanggal
group.member.addedIsang tao ay idinagdag sa isang grupo
group.member.removedIsang tao ay inalis mula sa isang grupo
donation.createdIsang regalo ay naitala — manual entry, online, o ang pending → complete transition
donation.updatedAng donation record ay nag-edit
attendance.recordedIsang pagbisita ay naka-log (manual entry o check-in)
session.createdIsang bagong attendance session ay nilikha (manu-mano o auto sa unang check-in)
form.submission.createdIsang form ay isinumite
event.createdIsang calendar event ay idinagdag
event.updatedIsang calendar event ay nag-edit
event.destroyedIsang calendar event ay natanggal

Payload Format

Bawat paghahatid ay isang HTTP POST na may isang JSON body at ang mga headers na ito:

HeaderDescription
Content-TypeLaging application/json
X-B1-EventAng event name, halimbawa person.created
X-B1-Delivery-IdNatatanging id para sa attempt ng paghahatid na ito — gamitin ito upang mag-deduplicate
X-B1-SignatureHMAC-SHA256 signature ng raw body (tingnan sa ibaba)
X-B1-TimestampUnix epoch seconds kapag ipinadala ang request
User-AgentB1-Webhooks/1.0

Ang body ay bumalot sa binagong resource sa isang maliit na envelope:

{
"event": "person.created",
"churchId": "AbC123XyZ90",
"occurredAt": "2026-05-17T14:32:08.114Z",
"data": {
"id": "Pq7Rs2Tu4Vw",
"churchId": "AbC123XyZ90",
"name": { "display": "Jordan Rivera", "first": "Jordan", "last": "Rivera" },
"contactInfo": { "email": "jordan@example.com" }
}
}

Para sa *.destroyed events, ang data ay naglalaman lamang ng id at churchId ng natanggal na record.

Ang mga event na ang mga payload ay sumusuporta sa ibang mga record sa pamamagitan ng id ay nagdadala din ng mga human-readable na pangalan, na nalutas sa oras ng paghahatid: personName at groupName sa mga group membership events, personName sa attendance, donation, at list membership events, groupName sa session.created, at formName (kasama ang personName kapag ang submission ay nakatali sa isang tao) sa form.submission.created.

Connector Types

Ang default na delivery format ay ang JSON envelope sa itaas — connectorType: "standard". Para sa Slack and Discord ang parehong webhook engine ay nagpo-post ng isang chat-shaped message na ang mga serbisyo na ito ay tumatanggap direkta:

connectorTypeBody sentUse when
"standard" (default){event, churchId, occurredAt, data} envelope, signedIkaw ay nagsusulat ng iyong sariling integration, o nakatutok sa Zapier / Make / isang custom server
"slack"{ "text": "💝 New donation: $50.00" }Ikaw ay naglalahad direkta sa isang Slack Incoming Webhook URL
"discord"{ "content": "💝 New donation: $50.00" }Ikaw ay naglalahad direkta sa isang Discord channel webhook URL
"mailchimp"n/a — ang connector ay tumatawag sa Mailchimp's API mismoGusto mo ng audience sync nang walang URL na i-host

Ang connector type ay nakatakda sa Connector Type dropdown sa webhook editor, o sa pamamagitan ng connectorType sa POST /membership/webhooks body. Ang signed X-B1-Signature header ay pa rin ipinapadala para sa Slack/Discord deliveries (hindi nila ito pinahahalagahan), kaya ang pagsisimula ng webhook pabalik sa standard mamaya ay hindi nangangailangan ng muling pag-sign.

Ang Slack at Discord ay purong body reshapes — ang engine ay pa rin POSTs sa church-supplied URL. Ang mailchimp ay ang una na connector na sa halip na nag-own ng kanya na HTTP exchange: bawat event ay naglalabas ito ng authenticated upsert/archive/tag requests laban sa Mailchimp's API (MailchimpConnector.deliver), at ang mga credentials nito ({apiKey, audienceId}) ay naka-store AES-encrypted sa webhooks.connectorConfig, write-only sa pamamagitan ng API. Ang mga Mailchimp webhooks ay tumatanggap lamang ng tao, group-member, at list-member events; ang save route ay nag-verify ng key at audience laban sa Mailchimp bago tanggapin. Ang mga delivery rows ay nag-store ng standard envelope, kaya ang delivery log ay nagpapakita kung ano ang nakita ng B1 kasama ng response ng Mailchimp. Ang mga unmapped na sitwasyon (tao na walang email, event na walang mapping) ay nagtatapos bilang successful na may Skipped: response body sa halip na masusunog ang retries.

Test Deliveries

Bawat webhook editor ay may Send Test Event button — ang kaugnay na API call ay POST /membership/webhooks/:id/test. Ang test route ay bumubuo ng synthetic payload para sa unang subscribed event, dispatch ito sa pamamagitan ng tunay na signed-delivery path (at sa pamamagitan ng formatForConnector para sa Slack/Discord), at nagbabalik ng nagreresultang delivery row na may kasamang responseStatus at responseBody. Gamitin ito upang kumpirmahin ang connectivity at signature handling bago i-flip ang integration on para sa tunay. Para sa mailchimp webhooks ang test ay sa halip ay nag-verify ng stored credentials laban sa Mailchimp API (ang isang synthetic event ay magsusulat ng isang fake subscriber sa tunay na audience ng church) at nagbabalik ng isang delivery-shaped result nang walang lumilikha ng row.

Pagsusuri ng Mga Signature

Laging i-verify ang X-B1-Signature bago magtiwala sa isang payload. Ang signature ay sha256= na sinusundan ng hex HMAC-SHA256 ng raw request body na may key na iyong signing secret. Kalkulahin ito sa mga byte na iyong natanggap — huwag muling i-serialize ang parsed JSON.

Node.js

const crypto = require("crypto");

function isValid(rawBody, signatureHeader, secret) {
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody, "utf8").digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader || "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Python

import hashlib, hmac

def is_valid(raw_body: bytes, signature_header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header or "")

PHP

function isValid(string $rawBody, string $signatureHeader, string $secret): bool {
$expected = "sha256=" . hash_hmac("sha256", $rawBody, $secret);
return hash_equals($expected, $signatureHeader ?? "");
}

Tanggihan ang anumang request na ang signature ay hindi tumugma. Opsyonal din na tanggihan ang mga request na ang X-B1-Timestamp ay higit sa ilang minuto ng lumang upang limitahan ang replay windows.

SDK Support

Para sa Node.js, ang @churchapps/integration-sdk ay naghahatid ng isang typed verifier at isang Express middleware na sumasagot sa raw-body capture, signature check, at envelope parsing para sa iyo:

import express from "express";
import { b1WebhookMiddleware } from "@churchapps/integration-sdk";

const app = express();
// Kunin ang raw body bago ang JSON parsing — kinakailangan upang ang signature ay manatiling nag-verify.
app.use(express.json({ verify: (req, _res, buf) => { (req as any).rawBody = buf; } }));

app.post("/webhooks/b1", b1WebhookMiddleware({ secret: process.env.B1_WEBHOOK_SECRET! }), (req, res) => {
const env = req.b1Webhook!;
switch (env.event) {
case "donation.created": console.log("new gift", env.data.amount); break;
}
res.sendStatus(200);
});

Ang SDK ay nagbalanse din ng WebhookVerifier.verify(secret, rawBody, signatureHeader) para sa non-Express runtimes (serverless functions, Fastify, atbp.). Tingnan ang package sa npm.

Delivery & Retries

Ang iyong endpoint ay dapat tumugon na may 2xx status kasing bilis ng posible — ideyal pagkatapos lamang ng pag-queue ng trabaho, hindi pagkatapos ng pagproseso. Anumang non-2xx response, isang failure ng koneksyon, o isang response na mas mabagal kaysa 10 seconds ay tumutok bilang isang nabigong paghahatid.

Ang mga nabigong deliveries ay muling sinusubok na may exponential backoff — 16 attempts sa loob ng humigit-kumulang 5 days. Ang interval ay lumalaki mula 1 minuto, sa pamamagitan ng mga oras, hanggang sa 3-day na mga agwat para sa mga final na pagsubok. Pagkatapos ng ika-16 na nabigong pagsubok ang paghahatid ay minarkahan exhausted at iniwan.

Ang paghahatid ay at-least-once: ang isang paghahatid ay maaaring dumating nang higit sa minsan (halimbawa, kung ang iyong endpoint ay successful ngunit ang response ay nawala). Gamitin ang X-B1-Delivery-Id header upang mag-deduplicate — iproseso ang bawat id lamang nang minsan at tratuhin ang repeats bilang no-ops.

Auto-disabling

Kung ang isang webhook ay gumagawa ng tatlong sunod-sunod na nakaubos na deliveries, B1 ay awtomatikong nag-disable nito. I-ayos ang iyong endpoint, pagkatapos ay muling i-enable ang webhook sa B1Admin (o sa pamamagitan ng POST /membership/webhooks na may "active": true).

Pagsusuri at Paghahatid Muli

Ang webhook editor sa B1Admin ay nagpapakita ng Recent Deliveries table — event, status, attempt count, response code, at timestamp. Ang pagpili ng isang row ay nagbubunyag ng buong payload na ipinadala at ang response na bumalik.

Gamitin ang Redeliver upang muling i-queue ang anumang nakaraang paghahatid na may kanya na orihinal na payload — kapaki-pakinabang pagkatapos ayusin ang isang bug sa iyong endpoint, o upang mag-backfill ng mga event na ang iyong endpoint ay nawala habang ito ay pababa.

URL Requirements

Dahil ang mga webhook URL ay church-supplied, B1 ay nag-enforce ng mga guards laban sa server-side request forgery. Ang isang webhook URL ay tinatanggihan — sa pagpaparehistro at nire-check muli bago ang bawat paghahatid — kung ito:

  • ay hindi gumagamit ng https
  • puntos sa localhost, isang .local / .internal hostname, o
  • nalulutas sa isang private, loopback, link-local, o cloud-metadata IP address

Ang iyong endpoint ay dapat na isang publicly reachable HTTPS service.