Documentación para desarrolladores

Webhooks

Cuando algo que su organización eligió cambia en Melora Connect, Melora Connect envía un POST HTTPS con un cuerpo JSON a su endpoint, firmado para que usted sepa que viene de Melora Connect.

Configurar un endpoint

  1. En Melora Connect, abra Settings y luego la pestaña Webhooks. Necesita permiso para administrar la configuración de la organización.
  2. 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.
  3. 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-Event es el tipo de evento, también en el cuerpo como type.
  • Melora-Event-Id es el id del evento, también en el cuerpo como id. Es el mismo en cada reintento: úselo para ignorar un mensaje que ya procesó.
  • Melora-Delivery identifica este mensaje a este endpoint y coincide con el registro de envíos de la pestaña Webhooks.
  • Melora-Signature lleva la firma, explicada más abajo.
  • organization_id es 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.

EventoCuándo se envíaCampos en data
member.createdSe 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.updatedCambian 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_changedCambia el estado de un miembro (active, lapsed, expired y los demás), con el estado anterior.member_id, status, previous_status
member.unsubscribedUn 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.resubscribedUn 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.createdAlguien 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.paidSe 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.completedSe 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
  1. Tome t (segundos Unix) y cada valor v1 del encabezado.
  2. Rechace el mensaje si t se 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.
  3. Calcule un HMAC-SHA-256, con su secreto de firma completo como clave (incluido el prefijo whsec_), sobre t, un punto y el cuerpo de la solicitud sin procesar, exactamente como llegó. Codifíquelo en hexadecimal.
  4. Acepte el mensaje si algún valor v1 es 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.

Véalo con sus propios datos

¿Listo para darles a sus miembros la plataforma que merecen?

Una demostración guiada con nuestro equipo, adaptada a cómo trabaja hoy su organización. En marcha desde 5 semanas después de la firma, con un especialista de Melora a cargo de la configuración.

Reserve una demoCalcule su fecha de puesta en marcha