Pular para o conteúdo principal

Webhooks

Webhooks permitem que uma igreja envie notificações em tempo real para ferramentas de terceiros — plataformas de automação (Zapier, Make, n8n), CRMs, sistemas de contabilidade ou qualquer coisa que aceite um POST HTTP. Quando uma pessoa, grupo ou família muda no B1, o B1 envia um payload JSON assinado para cada URL inscrita naquele evento.

Antes de Começar

  • Um administrador da igreja com a permissão Editar Configurações da Igreja registra e gerencia webhooks
  • Seu endpoint receptor deve ser acessível via HTTPS em um endereço público
  • Tenha uma forma de armazenar o segredo de assinatura com segurança — ele é mostrado apenas uma vez

Visão Geral

Webhooks são apenas de saída: B1 chama seu endpoint, você não chama o B1. Cada webhook é uma inscrição por igreja consistindo de uma URL de destino, um segredo de assinatura e uma lista de eventos inscritos.

A entrega usa uma outbox durável: quando um evento inscrito ocorre, o B1 registra uma linha de entrega e um worker em segundo plano faz POST dela em cerca de um minuto. Entregas falhadas são reexecutadas com backoff exponencial. Nada é perdido se uma entrega está lenta ou seu endpoint está temporariamente fora do ar.

Registrando um Webhook

No B1Admin

Vá para Configurações → Webhooks → Novo Webhook. Digite um nome, a URL do payload e selecione os eventos para se inscrever. Ao salvar, o segredo de assinatura é exibido uma vez — copie-o imediatamente e armazene-o com sua integração. Ele nunca é mostrado novamente (você pode rotacioná-lo mais tarde, mas não pode recuperar o original).

Via API

Todos os endpoints estão sob o caminho base do módulo Membership /membership/webhooks e requerem um JWT de um administrador da igreja com a permissão Settings / Edit.

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

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

A resposta de criação — e apenas a resposta de criação — inclui o secret:

{
"id": "a1b2c3d4e5f",
"name": "Zapier — novos membros",
"url": "https://hooks.zapier.com/hooks/catch/123/abc",
"events": ["person.created", "person.updated", "group.member.added"],
"active": true,
"secret": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822c"
}
Método e CaminhoPropósito
GET /membership/webhooksListar os webhooks da igreja (segredo omitido)
GET /membership/webhooks/eventsO catálogo de nomes de eventos válidos
GET /membership/webhooks/:idCarregar um webhook
POST /membership/webhooksCriar (sem id) ou atualizar (com id)
POST /membership/webhooks/:id/regenerate-secretRotacionar o segredo de assinatura; retorna o novo valor uma vez
DELETE /membership/webhooks/:idExcluir um webhook
GET /membership/webhooks/:id/deliveriesTentativas de entrega recentes para um webhook
GET /membership/webhooks/deliveries/:deliveryIdPayload completo e resposta para uma entrega
POST /membership/webhooks/deliveries/:deliveryId/redeliverReenfileirar uma entrega

Catálogo de Eventos

Nomes de eventos seguem o padrão {entidade}.{ação}. Busque a lista ao vivo de GET /membership/webhooks/events.

EventoDispara quando
person.createdUma pessoa é adicionada
person.updatedUm registro de pessoa é alterado
person.destroyedUma pessoa é excluída
household.createdUma família é adicionada
household.updatedUma família é alterada
household.destroyedUma família é excluída
group.createdUm grupo é adicionado
group.updatedUm grupo é alterado
group.destroyedUm grupo é excluído
group.member.addedUma pessoa é adicionada a um grupo
group.member.removedUma pessoa é removida de um grupo

Formato do Payload

Cada entrega é um POST HTTP com um corpo JSON e estes cabeçalhos:

CabeçalhoDescrição
Content-TypeSempre application/json
X-B1-EventO nome do evento, por exemplo person.created
X-B1-Delivery-IdId único para esta tentativa de entrega — use-o para deduplicar
X-B1-SignatureAssinatura HMAC-SHA256 do corpo bruto (veja abaixo)
X-B1-TimestampSegundos epoch Unix quando a solicitação foi enviada
User-AgentB1-Webhooks/1.0

O corpo envolve o recurso alterado em um pequeno 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 eventos *.destroyed, data contém apenas o id e churchId do registro excluído.

Verificando Assinaturas

Sempre verifique X-B1-Signature antes de confiar em um payload. A assinatura é sha256= seguida do hex HMAC-SHA256 do corpo bruto da solicitação com chave do seu segredo de assinatura. Calcule-o sobre os bytes que você recebeu — não serialize novamente o JSON analisado.

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 ?? "");
}

Rejeite qualquer solicitação cuja assinatura não corresponda. Opcionalmente, também rejeite solicitações cujo X-B1-Timestamp tem mais de alguns minutos para limitar janelas de replay.

Entrega e Reexecuções

Seu endpoint deve responder com um status 2xx o mais rápido possível — idealmente após apenas enfileirar o trabalho, não após processá-lo. Qualquer resposta não 2xx, uma falha de conexão ou uma resposta mais lenta que 10 segundos conta como uma entrega falha.

Entregas falhadas são reexecutadas com backoff exponencial — 16 tentativas ao longo de aproximadamente 5 dias. O intervalo cresce de 1 minuto, passando por horas, até lacunas de 3 dias para as tentativas finais. Após a 16ª tentativa falhada, a entrega é marcada como exhausted e abandonada.

A entrega é no mínimo uma vez: uma entrega pode chegar mais de uma vez (por exemplo, se seu endpoint tiver sucesso mas a resposta for perdida). Use o cabeçalho X-B1-Delivery-Id para deduplicar — processe cada id apenas uma vez e trate repetições como no-ops.

Desativação Automática

Se um webhook produzir três entregas esgotadas consecutivas, o B1 o desativa automaticamente. Corrija seu endpoint, depois reative o webhook no B1Admin (ou via POST /membership/webhooks com "active": true).

Inspecionando e Reenviando

O editor de webhook no B1Admin mostra uma tabela de Entregas Recentes — evento, status, contagem de tentativas, código de resposta e timestamp. Selecionar uma linha revela o payload completo que foi enviado e a resposta que voltou.

Use Reenviar para reenfileirar qualquer entrega passada com seu payload original — útil após corrigir um bug no seu endpoint, ou para preencher eventos que seu endpoint perdeu enquanto estava fora do ar.

Requisitos de URL

Como as URLs de webhook são fornecidas pela igreja, o B1 aplica proteções contra falsificação de solicitação do lado do servidor. Uma URL de webhook é rejeitada — no registro e reverificada antes de cada entrega — se ela:

  • não usa https
  • aponta para localhost, um nome de host .local / .internal, ou
  • resolve para um endereço IP privado, loopback, link-local ou de metadados de nuvem

Seu endpoint deve ser um serviço HTTPS publicamente acessível.