Перейти к основному содержимому

Веб-хуки

Веб-хуки позволяют церкви отправлять уведомления в реальном времени сторонним инструментам - платформам автоматизации (Zapier, Make, n8n), CRM, бухгалтерским системам или чему угодно, что принимает HTTP POST. Когда человек, группа или домохозяйство изменяются в B1, B1 отправляет подписанную JSON-полезную нагрузку на каждый URL, подписанный на это событие.

Прежде чем начать

  • Администратор церкви с разрешением Редактировать настройки церкви регистрирует и управляет веб-хуками
  • Ваша принимающая конечная точка должна быть доступна по HTTPS на публичном адресе
  • Имейте способ безопасно хранить секрет подписи - он показывается только один раз

Обзор

Веб-хуки являются только исходящими: B1 вызывает вашу конечную точку, вы не вызываете B1. Каждый веб-хук представляет собой подписку для каждой церкви, состоящую из URL назначения, секрета подписи и списка подписанных событий.

Доставка использует надежный почтовый ящик: когда происходит подписанное событие, B1 записывает строку доставки, и фоновый воркер отправляет ее POST-запросом примерно в течение минуты. Неудачные доставки повторяются с экспоненциальной задержкой. Ничего не теряется, если доставка медленная или ваша конечная точка временно недоступна.

Регистрация веб-хука

В B1Admin

Перейдите в Настройки → Веб-хуки → Новый веб-хук. Введите имя, URL полезной нагрузки и выберите события для подписки. При сохранении секрет подписи отображается один раз - скопируйте его немедленно и сохраните его с вашей интеграцией. Он никогда не показывается снова (вы можете его ротировать позже, но вы не можете получить оригинал).

Через API

Все конечные точки находятся под базовым путем модуля Membership /membership/webhooks и требуют JWT от администратора церкви с разрешением Settings / Edit.

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

{
"name": "Zapier — новые члены",
"url": "https://hooks.zapier.com/hooks/catch/123/abc",
"events": ["person.created", "person.updated", "group.member.added"]
}

Ответ на создание - и только ответ на создание - включает secret:

{
"id": "a1b2c3d4e5f",
"name": "Zapier — новые члены",
"url": "https://hooks.zapier.com/hooks/catch/123/abc",
"events": ["person.created", "person.updated", "group.member.added"],
"active": true,
"secret": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822c"
}
Метод и путьНазначение
GET /membership/webhooksСписок веб-хуков церкви (секрет опущен)
GET /membership/webhooks/eventsКаталог допустимых имен событий
GET /membership/webhooks/:idЗагрузить один веб-хук
POST /membership/webhooksСоздать (без id) или обновить (с id)
POST /membership/webhooks/:id/regenerate-secretРотировать секрет подписи; возвращает новое значение один раз
DELETE /membership/webhooks/:idУдалить веб-хук
GET /membership/webhooks/:id/deliveriesПоследние попытки доставки для веб-хука
GET /membership/webhooks/deliveries/:deliveryIdПолная полезная нагрузка и ответ для одной доставки
POST /membership/webhooks/deliveries/:deliveryId/redeliverПереставить доставку в очередь

Каталог событий

Имена событий следуют шаблону {сущность}.{действие}. Получите актуальный список из GET /membership/webhooks/events.

СобытиеСрабатывает когда
person.createdДобавлен человек
person.updatedИзменена запись человека
person.destroyedУдален человек
household.createdДобавлено домохозяйство
household.updatedИзменено домохозяйство
household.destroyedУдалено домохозяйство
group.createdДобавлена группа
group.updatedИзменена группа
group.destroyedУдалена группа
group.member.addedЧеловек добавлен в группу
group.member.removedЧеловек удален из группы

Формат полезной нагрузки

Каждая доставка представляет собой HTTP POST с телом JSON и следующими заголовками:

ЗаголовокОписание
Content-TypeВсегда application/json
X-B1-EventИмя события, например person.created
X-B1-Delivery-IdУникальный идентификатор для этой попытки доставки - используйте его для дедупликации
X-B1-SignatureПодпись HMAC-SHA256 необработанного тела (см. ниже)
X-B1-TimestampЭпоха Unix в секундах, когда был отправлен запрос
User-AgentB1-Webhooks/1.0

Тело оборачивает измененный ресурс в небольшой конверт:

{
"event": "person.created",
"churchId": "AbC123XyZ90",
"occurredAt": "2026-05-17T14:32:08.114Z",
"data": {
"id": "Pq7Rs2Tu4Vw",
"churchId": "AbC123XyZ90",
"name": { "display": "Джордан Ривера", "first": "Джордан", "last": "Ривера" },
"contactInfo": { "email": "jordan@example.com" }
}
}

Для событий *.destroyed, data содержит только id и churchId удаленной записи.

Проверка подписей

Всегда проверяйте X-B1-Signature перед тем, как доверять полезной нагрузке. Подпись - это sha256=, за которым следует шестнадцатеричный HMAC-SHA256 необработанного тела запроса, ключом которого является ваш секрет подписи. Вычисляйте его по полученным байтам - не пересериализуйте разобранный 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 ?? "");
}

Отклоняйте любой запрос, подпись которого не совпадает. По желанию также отклоняйте запросы, чей X-B1-Timestamp старше нескольких минут, чтобы ограничить окна воспроизведения.

Доставка и повторы

Ваша конечная точка должна отвечать со статусом 2xx как можно быстрее - в идеале после того, как только поставила работу в очередь, а не после ее обработки. Любой не-2xx ответ, сбой соединения или ответ медленнее 10 секунд считается неудачной доставкой.

Неудачные доставки повторяются с экспоненциальной задержкой - 16 попыток примерно за 5 дней. Интервал растет от 1 минуты, через часы, до 3-дневных промежутков для последних попыток. После 16-й неудачной попытки доставка помечается как exhausted и прекращается.

Доставка как минимум один раз: доставка может прийти более одного раза (например, если ваша конечная точка преуспела, но ответ был потерян). Используйте заголовок X-B1-Delivery-Id для дедупликации - обрабатывайте каждый идентификатор только один раз и рассматривайте повторы как неоперации.

Автоматическое отключение

Если веб-хук производит три последовательные исчерпанные доставки, B1 автоматически отключает его. Исправьте вашу конечную точку, затем снова включите веб-хук в B1Admin (или через POST /membership/webhooks с "active": true).

Инспектирование и повторная доставка

Редактор веб-хуков в B1Admin показывает таблицу Последние доставки - событие, статус, количество попыток, код ответа и временная метка. Выбор строки раскрывает полную отправленную полезную нагрузку и полученный ответ.

Используйте Повторная доставка, чтобы переставить любую прошлую доставку в очередь с ее исходной полезной нагрузкой - полезно после исправления ошибки в вашей конечной точке или для заполнения событий, которые ваша конечная точка пропустила, пока она была недоступна.

Требования к URL

Поскольку URL веб-хуков предоставляются церковью, B1 применяет защиту от подделки запроса на стороне сервера. URL веб-хука отклоняется - при регистрации и перепроверяется перед каждой доставкой - если он:

  • не использует https
  • указывает на localhost, имя хоста .local / .internal, или
  • разрешается в частный, обратный, локальный или облачный метаданные IP-адрес

Ваша конечная точка должна быть публично доступным HTTPS-сервисом.