Configurar un endpoint
- En Melora Connect, abra Settings y luego la pestaña Webhooks. Necesita permiso para administrar la configuración de la organización.
- Escriba la dirección https del endpoint, elija sus eventos y, si los quiere, marque Include member contact details. Luego haga clic en Add endpoint.
- Copie el secreto de firma y guárdelo como una contraseña. Se muestra una sola vez.
Una organización puede tener hasta 10 endpoints, como indica la sección 3.5 de los Términos de servicio. La misma pestaña pausa y reanuda un endpoint, envía un mensaje de prueba, emite un secreto nuevo y muestra cada mensaje con su resultado. Desde allí se puede reintentar un mensaje fallido, y el registro de envíos se conserva 30 días.
Un sistema también puede suscribir y cancelar su propio endpoint con una clave de API, mediante POST /webhooks y DELETE /webhooks/{id} de la API REST.
La dirección debe usar https, en el puerto 443 u 8443, con un nombre de host público. Las direcciones de una red privada, de loopback y de enlace local (incluidas las de metadatos de la nube) se rechazan al agregar el endpoint, y se verifican de nuevo antes de cada mensaje, porque el DNS de un host puede cambiar. Las redirecciones no se siguen.
Qué se envía
Cada mensaje es un POST con estos encabezados y un cuerpo JSON:
POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Melora-Webhooks/1.0
Melora-Signature: t=1791000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Melora-Event: member.status_changed
Melora-Event-Id: 8a1f0c1e-2f7b-4a55-9b3e-0d2b6f1c9e41
Melora-Delivery: 3c9d2a70-51f4-4e0b-8f7e-6a1d2c3b4e5f{
"id": "8a1f0c1e-2f7b-4a55-9b3e-0d2b6f1c9e41",
"type": "member.status_changed",
"created_at": "2026-10-01T14:03:22.418Z",
"organization_id": "5d0c7a4e-1b2f-4c3d-9e8f-0a1b2c3d4e5f",
"data": {
"member_id": "0f9e8d7c-6b5a-4938-8271-605f4e3d2c1b",
"status": "lapsed",
"previous_status": "active"
}
}Melora-Eventes el tipo de evento, también en el cuerpo comotype.Melora-Event-Ides el id del evento, también en el cuerpo comoid. Es el mismo en cada reintento: úselo para ignorar un mensaje que ya procesó.Melora-Deliveryidentifica este mensaje a este endpoint y coincide con el registro de envíos de la pestaña Webhooks.Melora-Signaturelleva la firma, explicada más abajo.organization_ides su organización. Un mensaje solo lleva registros de su propia organización.
Eventos
Cada endpoint recibe los eventos elegidos para él. Los datos de contacto de los miembros se omiten salvo que el endpoint tenga marcada la opción Include member contact details: sin ella, no se envían email, phone, city, state, zip, company, title, industry, website, linkedin_url, tags, show_in_directory, donor_email, guest_email, y un mensaje member.updated cuyos únicos cambios fueron en esos campos no se envía. La tabla muestra los campos con los datos de contacto activados.
| Evento | Cuándo se envía | Campos en data |
|---|---|---|
member.created | Se crea un registro de miembro. | member: id, status, name, first_name, last_name, email, phone, company, title, industry, city, state, zip, website, linkedin_url, tier, membership_type_id, membership_category, chapter_id, corporate_membership_id, join_date, renewal_date, tags, show_in_directory, archived_at, user_id |
member.updated | Cambian los datos de contacto o de membresía de un miembro. changed contiene solo los campos del miembro que cambiaron, con sus valores nuevos. | member_id, changed |
member.status_changed | Cambia el estado de un miembro (active, lapsed, expired y los demás), con el estado anterior. | member_id, status, previous_status |
member.unsubscribed | Un miembro se da de baja de su email de marketing: un enlace de baja, una queja, su interruptor de email, Mailchimp o la API. Se envía una vez, solo cuando la baja cambia; email_marketing_opted_out_at indica cuándo. | member_id, email_marketing_opt_out, email_marketing_opted_out_at, email |
member.resubscribed | Un miembro que se había dado de baja puede volver a recibir su email de marketing, por ejemplo tras reactivarlo él mismo. Se envía una vez, solo cuando la baja cambia; email_marketing_opted_out_at es null. | member_id, email_marketing_opt_out, email_marketing_opted_out_at, email |
event.registration.created | Alguien se inscribe en uno de sus eventos. | registration: id, event_id, user_id, status, tier_id, guest_name, guest_email, group_order_id, created_at |
dues_invoice.paid | Se paga una factura de cuotas, en línea o registrada por el personal. previous_status es el estado anterior de la factura. | invoice: id, invoice_number, member_id, user_id, dues_subscription_id, amount_cents, currency, paid_at, paid_via, period_start, period_end, due_at; previous_status |
donation.completed | Se registra una donación o se completa una donación con tarjeta. Su estado es received o completed. Para un donante anónimo se omiten donor_name, donor_email y user_id. | donation: id, amount_cents, currency, status, fundraiser_id, campaign, designation, received_at, source, anonymous, donor_name, donor_email, user_id |
webhook.test se envía solo con el botón Send test, con data.message.
Verificar la firma
El encabezado Melora-Signature tiene esta forma:
Melora-Signature: t=1791000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd- Tome
t(segundos Unix) y cada valorv1del encabezado. - Rechace el mensaje si
tse aleja más de 300 segundos de su reloj. La marca de tiempo forma parte de lo firmado, así que esto impide que se reenvíe un mensaje viejo. - Calcule un HMAC-SHA-256, con su secreto de firma completo como clave (incluido el prefijo
whsec_), sobret, un punto y el cuerpo de la solicitud sin procesar, exactamente como llegó. Codifíquelo en hexadecimal. - Acepte el mensaje si algún valor
v1es igual a su resultado. Compare en tiempo constante.
Verifique el cuerpo sin procesar antes de interpretarlo: volver a serializar el JSON cambia los bytes, y la firma no coincidirá.
Node.js
import crypto from "node:crypto";
// header: the Melora-Signature request header
// rawBody: the request body exactly as received, as a string (before JSON.parse)
// secret: the endpoint's signing secret (it starts with whsec_)
export function verifyMeloraSignature(header, rawBody, secret, toleranceSeconds = 300) {
const parts = String(header || "").split(",").map((p) => p.trim());
const t = parts.find((p) => p.startsWith("t="))?.slice(2);
const signatures = parts.filter((p) => p.startsWith("v1=")).map((p) => p.slice(3));
if (!t || !/^\d+$/.test(t) || signatures.length === 0) return false;
if (Math.abs(Date.now() / 1000 - Number(t)) > toleranceSeconds) return false;
const expected = crypto.createHmac("sha256", secret).update(t + "." + rawBody).digest("hex");
return signatures.some(
(s) =>
s.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected)),
);
}Python
import hashlib
import hmac
import time
def verify_melora_signature(header, raw_body, secret, tolerance_seconds=300):
"""header: the Melora-Signature header. raw_body: the request body as bytes,
exactly as received. secret: the endpoint's signing secret (it starts with whsec_)."""
parts = [p.strip() for p in (header or "").split(",")]
t = next((p[2:] for p in parts if p.startswith("t=")), None)
signatures = [p[3:] for p in parts if p.startswith("v1=")]
if not t or not t.isdigit() or not signatures:
return False
if abs(time.time() - int(t)) > tolerance_seconds:
return False
signed = t.encode() + b"." + raw_body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(s, expected) for s in signatures)Respuestas, reintentos y orden
Responda con cualquier estado 2xx en menos de 8 segundos. Cualquier otra respuesta, un tiempo agotado o una redirección cuenta como un intento fallido, y el mensaje se reintenta después de 1 minuto, 5 minutos, 30 minutos, 2 horas y 6 horas: 6 intentos en total. Después queda marcado como fallido en el registro de envíos, donde el personal puede reintentarlo.
Si 5 mensajes seguidos fallan en todos sus intentos, Melora Connect pausa el endpoint, lo indica en la pestaña Webhooks y avisa una vez al personal que administra la configuración de la organización, en la aplicación y por correo. Mientras un endpoint está pausado no se ponen mensajes nuevos en cola para él; los mensajes que ya esperaban se envían cuando se reanuda.
No se garantiza que los mensajes lleguen en el orden en que ocurrieron los cambios, y un mensaje puede llegar más de una vez. Use created_at y el id del evento, y lea el registro actual mediante la API REST cuando el orden importe.
Cambiar el secreto
Al emitir un secreto nuevo en la pestaña Webhooks, el nuevo secreto de firma se muestra una vez. Durante las 24 horas siguientes, cada mensaje lleva dos valores v1, uno para el secreto nuevo y otro para el anterior, para que pueda cambiar sin perder ningún mensaje. Después, solo firma el secreto nuevo.

Personas · Eventos · Comunidades

