Documentation développeurs

Webhooks

Quand quelque chose que votre organisation a choisi change dans Melora Connect, Melora Connect envoie un POST HTTPS avec un corps JSON à votre endpoint, signé pour que vous sachiez qu'il vient de Melora Connect.

Configurer un endpoint

  1. Dans Melora Connect, ouvrez Settings puis l'onglet Webhooks. Vous avez besoin de la permission de gérer les paramètres de l'organisation.
  2. Saisissez l'adresse https de l'endpoint, choisissez ses événements et, si vous les voulez, cochez Include member contact details. Cliquez ensuite sur Add endpoint.
  3. Copiez le secret de signature et conservez-le comme un mot de passe. Il n'est affiché qu'une fois.

Une organisation peut avoir jusqu'à 10 endpoints, comme l'indique la section 3.5 des Conditions d'utilisation. Le même onglet met en pause et réactive un endpoint, envoie un message de test, émet un nouveau secret et affiche chaque message avec son résultat. Un message en échec peut y être renvoyé, et le journal des envois est conservé 30 jours.

Un système peut aussi abonner et désabonner son propre endpoint avec une clé d'API, par POST /webhooks et DELETE /webhooks/{id} de l'API REST.

L'adresse doit utiliser https, sur le port 443 ou 8443, avec un nom d'hôte public. Les adresses d'un réseau privé, de boucle locale et de lien local (y compris les adresses de métadonnées cloud) sont refusées à l'ajout de l'endpoint, puis vérifiées à nouveau avant chaque message, car le DNS d'un hôte peut changer. Les redirections ne sont pas suivies.

Ce qui est envoyé

Chaque message est un POST avec ces en-têtes et un corps 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 est le type d'événement, aussi présent dans le corps comme type.
  • Melora-Event-Id est l'id de l'événement, aussi présent dans le corps comme id. Il reste le même à chaque nouvelle tentative : utilisez-le pour ignorer un message déjà traité.
  • Melora-Delivery identifie ce message pour cet endpoint et correspond au journal des envois de l'onglet Webhooks.
  • Melora-Signature porte la signature, expliquée plus bas.
  • organization_id est votre organisation. Un message ne porte que les enregistrements de votre propre organisation.

Événements

Chaque endpoint reçoit les événements choisis pour lui. Les coordonnées des membres sont omises sauf si l'option Include member contact details est cochée pour l'endpoint : sans elle, email, phone, city, state, zip, company, title, industry, website, linkedin_url, tags, show_in_directory, donor_email, guest_email ne sont pas envoyés, et un message member.updated dont les seuls changements portaient sur ces champs n'est pas envoyé du tout. Le tableau montre les champs avec les coordonnées activées.

ÉvénementQuand il est envoyéChamps de data
member.createdUne fiche de membre est créée.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.updatedLes coordonnées ou les données d'adhésion d'un membre changent. changed ne contient que les champs du membre qui ont changé, avec leurs nouvelles valeurs.member_id, changed
member.status_changedLe statut d'un membre change (active, lapsed, expired et les autres), avec le statut précédent.member_id, status, previous_status
member.unsubscribedUn membre se désinscrit de vos emails marketing : un lien de désinscription, une plainte, son interrupteur d'email, Mailchimp ou l'API. Envoyé une fois, seulement quand la désinscription change ; email_marketing_opted_out_at indique quand.member_id, email_marketing_opt_out, email_marketing_opted_out_at, email
member.resubscribedUn membre qui s'était désinscrit peut de nouveau recevoir vos emails marketing, par exemple après les avoir réactivés lui-même. Envoyé une fois, seulement quand la désinscription change ; email_marketing_opted_out_at vaut null.member_id, email_marketing_opt_out, email_marketing_opted_out_at, email
event.registration.createdQuelqu'un s'inscrit à l'un de vos événements.registration: id, event_id, user_id, status, tier_id, guest_name, guest_email, group_order_id, created_at
dues_invoice.paidUne facture de cotisation est payée, en ligne ou enregistrée par le personnel. previous_status est le statut précédent de la facture.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.completedUn don est enregistré ou un don par carte aboutit. Son statut est received ou completed. Pour un donateur anonyme, donor_name, donor_email et user_id sont omis.donation: id, amount_cents, currency, status, fundraiser_id, campaign, designation, received_at, source, anonymous, donor_name, donor_email, user_id

webhook.test n'est envoyé que par le bouton Send test, avec data.message.

Vérifier la signature

L'en-tête Melora-Signature se présente ainsi :

Melora-Signature: t=1791000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
  1. Prenez t (secondes Unix) et chaque valeur v1 de l'en-tête.
  2. Refusez le message si t s'écarte de plus de 300 secondes de votre horloge. L'horodatage fait partie de ce qui est signé : c'est ce qui empêche de rejouer un ancien message.
  3. Calculez un HMAC-SHA-256, avec votre secret de signature complet comme clé (préfixe whsec_ compris), sur t, un point, puis le corps brut de la requête exactement tel que reçu. Encodez-le en hexadécimal.
  4. Acceptez le message si une valeur v1 est égale à votre résultat. Comparez en temps constant.

Vérifiez le corps brut avant de l'analyser : resérialiser le JSON change les octets, et la signature ne correspondra pas.

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)

Réponses, nouvelles tentatives et ordre

Répondez avec n'importe quel statut 2xx en moins de 8 secondes. Toute autre réponse, un délai dépassé ou une redirection compte comme une tentative échouée, et le message est renvoyé après 1 minute, 5 minutes, 30 minutes, 2 heures et 6 heures : 6 tentatives au total. Ensuite, il est marqué en échec dans le journal des envois, où le personnel peut le renvoyer.

Si 5 messages de suite échouent à toutes leurs tentatives, Melora Connect met l'endpoint en pause, l'indique dans l'onglet Webhooks et prévient une fois le personnel qui gère les paramètres de l'organisation, dans l'application et par e-mail. Pendant la pause, aucun nouveau message n'est mis en file pour cet endpoint ; les messages déjà en attente sont envoyés quand il est réactivé.

L'ordre d'arrivée des messages ne suit pas forcément celui des changements, et un message peut arriver plus d'une fois. Utilisez created_at et l'id de l'événement, et lisez l'enregistrement actuel par l'API REST quand l'ordre compte.

Changer le secret

Émettre un nouveau secret dans l'onglet Webhooks affiche le nouveau secret de signature une fois. Pendant les 24 heures suivantes, chaque message porte deux valeurs v1, une pour le nouveau secret et une pour l'ancien, pour que vous puissiez basculer sans manquer de message. Ensuite, seul le nouveau secret signe.

Découvrez-le avec vos propres données

Prêt à offrir à vos membres la plateforme qu'ils méritent ?

Une présentation guidée avec notre équipe, adaptée au fonctionnement actuel de votre organisation. En service dès 5 semaines après la signature, avec un spécialiste Melora pour la mise en place.

Réserver une démoEstimez votre date de mise en service