Matchai Docs

WebhooksBientôt disponible

Recevez des événements signés quand un prospect arrive, qu'une visite est réservée ou qu'une inscription change. Catalogue, signature, tests et journaux.

Bientôt disponible. Cette fonctionnalité est documentée avant son lancement pour que vous puissiez planifier votre intégration; elle n’est pas encore offerte.

Un abonnement webhook indique à Matchai où envoyer les événements. Quand quelque chose se produit dans un compte Matchai connecté, comme un nouveau prospect, une visite réservée ou une inscription publiée, Matchai envoie une requête POST signée à votre point de terminaison, avec une description JSON de ce qui s'est passé.

Les webhooks remplacent l'interrogation périodique. Votre application apprend qu'un changement a eu lieu au moment où il se produit, au lieu de redemander sans cesse, et la signature prouve que le message vient de Matchai.

Fonctionnement

S'abonner. Votre application appelle POST /webhook-subscriptions avec l'adresse https de votre point de terminaison et les événements voulus. La réponse contient le secret de signature, affiché une seule fois.

Recevoir. Chaque fois qu'un événement correspondant se produit, Matchai envoie un POST à votre point de terminaison. Le corps est un Event en JSON. Trois en-têtes l'accompagnent : Matchai-Signature, Matchai-Event-Id et Matchai-Delivery-Attempt.

Vérifier et répondre. Votre point de terminaison vérifie la signature, répond avec n'importe quel statut 2xx en moins de 10 secondes et fait le vrai travail ensuite.

Réessayer. Une livraison qui n'obtient pas de 2xx est renvoyée à des intervalles de plus en plus longs, pendant au plus 3 jours.

Un abonnement appartient à une seule connexion, c'est-à-dire à un compte Matchai et à une application. Une application qui sert trois professionnels détient trois connexions et crée des abonnements pour chacune. Consultez Se connecter avec Matchai et Clés d'API pour savoir comment une connexion est établie.

S'abonner à des événements

La création d'un abonnement exige la portée webhooks:manage. Les webhooks sont inclus avec la connexion, et ils n'utilisent aucune énergie.

Champ

Type

Envoyez un en-tête Idempotency-Key avec la requête. Une nouvelle tentative avec la même clé dans les 24 heures renvoie la première réponse au lieu de créer un deuxième abonnement.

curl https://api.matchai.co/v1/webhook-subscriptions \
  -H "Authorization: Bearer $MATCHAI_API_KEY" \
  -H "Idempotency-Key: 6f1d3c2a-0b7e-4f5a-9c8d-1e2f3a4b5c6d" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://maisonweb.example.com/hooks/matchai",
    "events": ["lead.created", "appointment.booked"],
    "description": "Leads and visits for camilletremblay.ca"
  }'

La réponse est 201 Created. Le secret figure dans cette réponse une seule fois, et plus jamais ensuite : conservez-le tout de suite.

{
  "id": "8b9c0d1e-2f3a-4b4c-9d5e-6f7a8b9c0d1e",
  "url": "https://maisonweb.example.com/hooks/matchai",
  "description": "Leads and visits for camilletremblay.ca",
  "events": ["lead.created", "appointment.booked"],
  "status": "enabled",
  "disabled_reason": null,
  "created_at": "2026-10-10T18:05:00Z",
  "updated_at": "2026-10-10T18:05:00Z",
  "secret": "whsec_4fT9qLmZ2xVb7NcR8sKd3HwPj6YeA1uG"
}

Le secret de signature commence par whsec_. Gardez-le dans votre coffre de secrets, jamais dans le code ni dans les journaux. Si vous le perdez, renouvelez-le.

Vous ne recevez que des événements sur les données que vos portées vous permettent de lire. La dernière colonne du catalogue indique la portée qu'exige chaque événement : demandez ces portées quand le titulaire du compte connecte votre application.

Gérer les abonnements

Toutes ces opérations exigent webhooks:manage.

PourAppelRéférence
Lister vos abonnementsGET /webhook-subscriptionslistWebhookSubscriptions
Lire un abonnement (le secret n'est plus jamais renvoyé)GET /webhook-subscriptions/{webhook_subscription_id}getWebhookSubscription
Changer le point de terminaison ou les événements, ou désactiver ou réactiver l'abonnementPATCH /webhook-subscriptions/{webhook_subscription_id}updateWebhookSubscription
Cesser d'envoyer des événements à un point de terminaisonDELETE /webhook-subscriptions/{webhook_subscription_id}deleteWebhookSubscription
Émettre un nouveau secret de signaturePOST /webhook-subscriptions/{webhook_subscription_id}/rotate-secretrotateWebhookSecret
Envoyer un événement de test maintenantPOST /webhook-subscriptions/{webhook_subscription_id}/testtestWebhookSubscription
Voir les tentatives de livraisonGET /webhook-subscriptions/{webhook_subscription_id}/deliverieslistWebhookDeliveries
Renvoyer une livraison échouéePOST /webhook-subscriptions/{webhook_subscription_id}/deliveries/{delivery_id}/retryretryWebhookDelivery
Rattraper les événements passésGET /events et GET /events/{event_id}listEvents, getEvent

Supprimer un abonnement arrête les événements pour ce point de terminaison. Rien n'est supprimé dans le compte Matchai.

Un abonnement comporte ces champs :

Champ

Type

Catalogue des événements

Voici les types d'événements, regroupés par sujet. Chaque nom mène à sa page de référence, qui montre la charge utile.

De nouveaux types d'événements peuvent apparaître

Matchai peut ajouter des types d'événements, et de nouveaux champs aux événements existants, sans nouvelle version de l'API. Ignorez tout type d'événement que vous ne connaissez pas et tout champ que vous n'utilisez pas.

Contacts et prospects

ÉvénementEnvoyé quanddata.objectPortée requise
lead.createdUn nouveau prospect est arrivé dans le compte : une demande de renseignements à partir d'une inscription, du site Matchai, de la Page Matchai ou de votre site Web, une demande de visite, ou une personne qui a ajouté une inscription à ses favoris.Le contactcontacts:read
contact.createdTout nouveau contact, quel que soit son type. Un nouveau prospect envoie aussi lead.created.Le contactcontacts:read
contact.updatedLes détails d'un contact ou ses détails de prospect ont changé. data.previous contient les anciennes valeurs des champs qui ont changé.Le contactcontacts:read
contact.convertedUn prospect est devenu client, ou un client est devenu professionnel. data.previous.type contient l'ancien type.Le contactcontacts:read
contact.deletedUn contact a été supprimé.Un objet supprimécontacts:read

Conversations et messages

ÉvénementEnvoyé quanddata.objectPortée requise
conversation.createdUne nouvelle conversation dont le compte fait partie.La conversationmessages:read
message.receivedQuelqu'un d'autre a écrit dans une conversation dont le compte fait partie.Le messagemessages:read
message.sentLe compte a envoyé un message, depuis l'application, l'Assistant Matchai ou une application comme la vôtre.Le messagemessages:read

Rendez-vous

ÉvénementEnvoyé quanddata.objectPortée requise
appointment.requestedQuelqu'un a demandé un moment au titulaire du compte. La demande attend sa réponse.Le rendez-vouscalendar:read
appointment.bookedUn rendez-vous est confirmé : il a été réservé directement, ou une demande a été acceptée.Le rendez-vouscalendar:read
appointment.rescheduledUn rendez-vous a une nouvelle heure, ou une nouvelle heure a été proposée (status: reschedule_pending).Le rendez-vouscalendar:read
appointment.cancelledUn rendez-vous a été annulé, ou une demande a été refusée.Le rendez-vouscalendar:read

Inscriptions

ÉvénementEnvoyé quanddata.objectPortée requise
listing.createdUne nouvelle inscription en brouillon, créée à la main, par la création par IA ou par importation.L'inscriptionlistings:read
listing.updatedLes champs, les photos ou les pièces d'une inscription ont changé. data.previous contient les anciennes valeurs des champs de premier niveau qui ont changé.L'inscriptionlistings:read
listing.publishedUne inscription a été mise en ligne sur le Web public.L'inscriptionlistings:read
listing.unpublishedUne inscription est redevenue un brouillon.L'inscriptionlistings:read
listing.archivedUne inscription a été retirée du marché.L'inscriptionlistings:read
listing.deletedUne inscription a été supprimée.Un objet supprimélistings:read
listing.savedQuelqu'un a ajouté l'une des inscriptions du compte à ses favoris. data.object.contact est votre fiche de contact pour cette personne.L'inscription, le contact et saved_atlistings:read et contacts:read
listing.generation.completedUne création d'inscription par IA a réussi ou échoué.La génération d'inscriptionlistings:read

Documents, invitations et notifications

ÉvénementEnvoyé quanddata.objectPortée requise
document.createdUn document a été ajouté à Matchai Cloud, par téléversement, par courriel ou au moyen d'un lien de dépôt.Le documentdocuments:read
drop_link.upload_receivedQuelqu'un a téléversé un fichier au moyen de l'un des liens de dépôt du compte.Le lien de dépôt et le documentdocuments:read
invitation.acceptedQuelqu'un a accepté une invitation envoyée par le compte.L'invitationteams:read ou projects:read
notification.createdLe compte a reçu une notification.La notificationnotifications:read

Approbations et tests

ÉvénementEnvoyé quanddata.objectPortée requise
approval.resolvedUne action finale demandée par votre application a été approuvée (et effectuée), refusée ou a expiré. data.object.result contient le résultat.L'approbationAucune. L'événement va à l'application qui a fait la demande.
webhook.testVous l'avez envoyé avec POST /webhook-subscriptions/{webhook_subscription_id}/test.Un message fixewebhooks:manage

Un objet supprimé a la forme { "id": "…", "object": "listing", "deleted": true }, où object vaut contact ou listing.

À quoi ressemble une livraison

Chaque livraison est un POST avec un corps JSON et ces en-têtes.

En-têteSignification
Matchai-SignatureLa signature à vérifier. Elle ressemble à t=1792177965,v1=5257a869…. Pendant un renouvellement du secret, elle porte deux valeurs v1.
Matchai-Event-IdLa même valeur que l'id du corps. Servez-vous-en pour ignorer les doublons.
Matchai-Delivery-AttemptLe numéro de la tentative, à partir de 1.

Le corps est le même objet Event pour tous les types. GET /events renvoie les mêmes objets.

Champ

Type

Une livraison lead.created, abrégée aux champs qu'utilisent la plupart des applications :

{
  "id": "0d1e2f3a-4b5c-4d6e-9f7a-8b9c0d1e2f3a",
  "type": "lead.created",
  "created_at": "2026-10-16T19:12:45Z",
  "api_version": "v1",
  "test_mode": false,
  "account": {
    "user_id": "3f1c9a2e-7b44-4d0e-9c1a-5e8f2b6d4a10",
    "professional_id": "8a2d4f6b-1c3e-4a5b-9d7f-0e2c4b6a8d1f"
  },
  "data": {
    "object": {
      "id": "c5d8e1f2-3a4b-4c6d-9e8f-7a6b5c4d3e2f",
      "type": "lead",
      "first_name": "Julien",
      "last_name": "Bouchard",
      "display_name": "Julien Bouchard",
      "email": "julien.bouchard@example.com",
      "phone": "+15145550142",
      "notes": "Looking for a 2-bedroom on the Plateau, close to the orange line.",
      "source": "listing",
      "source_listing_id": "b7e4c2a1-9f3d-4e6b-8a5c-1d2f3e4a5b6c",
      "conversation_id": "d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a",
      "created_at": "2026-10-16T19:12:44Z",
      "updated_at": "2026-10-17T16:20:00Z"
    }
  }
}

Les noms de champs sont en snake_case, les identifiants sont des chaînes opaques, et un événement ne contient que les données que vos portées vous permettent de lire.

Vérifier la signature

N'importe qui peut envoyer une requête à votre point de terminaison. Vérifiez Matchai-Signature à chaque livraison avant de vous fier au corps.

L'en-tête ressemble à ceci :

Matchai-Signature: t=1792177965,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

Prenez t (en secondes Unix) et le corps brut de la requête, exactement tel que vous l'avez reçu, avant toute analyse JSON.

Calculez le HMAC-SHA256 de <t>.<raw body> avec le secret de signature de votre abonnement (whsec_…), en hexadécimal minuscule. Le texte signé est l'horodatage, un point, puis le corps.

Comparez le résultat à chaque valeur v1 en temps constant. Acceptez la livraison si l'une d'elles correspond.

Refusez la livraison si t s'écarte de plus de 5 minutes de votre horloge. Cela empêche quelqu'un de rejouer une ancienne requête.

Utilisez le corps brut

Signez et comparez les octets que Matchai a envoyés. Un corps analysé puis réécrit peut différer par ses espaces ou par l'ordre de ses clés, et alors aucune signature ne correspondra. Dans la plupart des cadriciels Web, cela veut dire lire le corps comme du texte ou des octets avant l'exécution de tout intergiciel (middleware) JSON.

Utilisez le secret exactement comme vous l'avez reçu, y compris le préfixe whsec_.

verify-signature.ts
import { createHmac, timingSafeEqual } from 'node:crypto';

const TOLERANCE_SECONDS = 5 * 60;

/**
 * Checks a Matchai-Signature header against the raw request body.
 * Returns true only when the timestamp is fresh and one v1 signature matches.
 */
export function verifyMatchaiSignature(
  rawBody: string,
  header: string | null | undefined,
  secret: string,
  nowSeconds: number = Math.floor(Date.now() / 1000),
): boolean {
  if (!header) return false;

  // Header: t=1792177965,v1=<hex>[,v1=<hex>]
  let timestamp = '';
  const signatures: string[] = [];
  for (const part of header.split(',')) {
    const separator = part.indexOf('=');
    if (separator === -1) continue;
    const name = part.slice(0, separator).trim();
    const value = part.slice(separator + 1).trim();
    if (name === 't') timestamp = value;
    else if (name === 'v1') signatures.push(value);
  }

  const sentAt = Number(timestamp);
  if (!Number.isInteger(sentAt) || signatures.length === 0) return false;

  // Reject a timestamp more than 5 minutes from your clock. This stops replays.
  if (Math.abs(nowSeconds - sentAt) > TOLERANCE_SECONDS) return false;

  // HMAC-SHA256 of "<t>.<raw body>" with the signing secret, as lowercase hex.
  const expected = Buffer.from(
    createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex'),
  );

  // Compare with every v1 value in constant time. One match is enough.
  return signatures.some((candidate) => {
    const received = Buffer.from(candidate);
    return received.length === expected.length && timingSafeEqual(received, expected);
  });
}

Vérifier votre code avec une signature connue

Cet exemple est fictif. Servez-vous-en pour vérifier que votre code calcule la signature de la même façon que Matchai, avant de le brancher sur un véritable abonnement.

Test vector
secret: whsec_test_0123456789abcdef
header: t=1792177965,v1=f222c924e7438518b6cec8b46239f468624169721a58570efc3084921aa229c1
body:   {"id":"0d1e2f3a-4b5c-4d6e-9f7a-8b9c0d1e2f3a","type":"webhook.test","created_at":"2026-10-16T19:12:45Z","api_version":"v1","test_mode":true,"account":{"user_id":"3f1c9a2e-7b44-4d0e-9c1a-5e8f2b6d4a10","professional_id":null},"data":{"object":{"message":"This is a test event from Matchai."}}}

L'exemple est ancien : passez donc son propre horodatage comme heure actuelle. Ainsi, la vérification de la fenêtre de 5 minutes ne sera pas la cause d'un échec.

verify-signature.test.ts
import assert from 'node:assert/strict';
import { verifyMatchaiSignature } from './verify-signature.js';

const secret = 'whsec_test_0123456789abcdef';
const header =
  't=1792177965,v1=f222c924e7438518b6cec8b46239f468624169721a58570efc3084921aa229c1';
const body =
  '{"id":"0d1e2f3a-4b5c-4d6e-9f7a-8b9c0d1e2f3a","type":"webhook.test","created_at":"2026-10-16T19:12:45Z","api_version":"v1","test_mode":true,"account":{"user_id":"3f1c9a2e-7b44-4d0e-9c1a-5e8f2b6d4a10","professional_id":null},"data":{"object":{"message":"This is a test event from Matchai."}}}';

assert.equal(verifyMatchaiSignature(body, header, secret, 1792177965), true);
assert.equal(verifyMatchaiSignature(`${body} `, header, secret, 1792177965), false); // body changed
assert.equal(verifyMatchaiSignature(body, header, secret, 1792177965 + 301), false); // too old
console.log('The signature check works.');

Recevoir une livraison

Répondez avec n'importe quel statut 2xx en moins de 10 secondes. Toute autre réponse, ou une absence de réponse à temps, compte comme une livraison échouée et entraîne une nouvelle tentative. Alors vérifiez, enregistrez l'événement ou mettez-le en file d'attente, répondez, et faites le travail long ensuite.

Un gestionnaire pour tout environnement d'exécution qui utilise les objets standards Request et Response.

webhook-handler.ts
import { verifyMatchaiSignature } from './verify-signature.js';

type MatchaiEvent = {
  id: string;
  type: string;
  created_at: string;
  api_version: 'v1';
  test_mode: boolean;
  account: { user_id: string; professional_id: string | null };
  data: { object: Record<string, unknown>; previous?: Record<string, unknown> };
};

const secret = process.env.MATCHAI_WEBHOOK_SECRET ?? '';

// Use a database with a unique key on the event id in production.
const seenEventIds = new Set<string>();

export async function handleMatchaiWebhook(request: Request): Promise<Response> {
  // Read the body as text first. Do not parse JSON before you verify.
  const rawBody = await request.text();

  if (!verifyMatchaiSignature(rawBody, request.headers.get('Matchai-Signature'), secret)) {
    return new Response('Invalid signature', { status: 400 });
  }

  const event = JSON.parse(rawBody) as MatchaiEvent;

  // A delivery can arrive more than once. Acknowledge duplicates and do nothing.
  if (seenEventIds.has(event.id)) return new Response(null, { status: 200 });
  seenEventIds.add(event.id);

  // Answer now and do the slow work afterwards. In production, put the event on a queue.
  void processEvent(event).catch((error) => console.error('Event failed', event.id, error));
  return new Response(null, { status: 200 });
}

async function processEvent(event: MatchaiEvent): Promise<void> {
  switch (event.type) {
    case 'lead.created':
      console.log('New lead', event.data.object.id);
      break;
    case 'appointment.booked':
      console.log('Visit booked', event.data.object.id);
      break;
    default:
      // Ignore event types you do not know. New ones can appear at any time.
      break;
  }
}

Si une même application sert plusieurs comptes, donnez à chaque abonnement sa propre adresse, par exemple /hooks/matchai/{your broker id}. Chaque abonnement a son propre secret, et l'adresse vous indique avec quel secret vérifier avant de lire le corps.

Nouvelles tentatives et échecs

Une livraison qui n'obtient pas de 2xx est retentée à des intervalles de plus en plus longs : environ 1 minute, 5 minutes, 30 minutes, 2 heures et 6 heures, puis toutes les 12 heures, pendant au plus 3 jours. Matchai-Delivery-Attempt vous indique de quelle tentative il s'agit.

Si toutes les livraisons à un abonnement échouent pendant 3 jours, Matchai désactive l'abonnement. Celui-ci affiche alors disabled_reason: failing, et le développeur de l'application en est avisé par courriel.

Le réactiver avec PATCH /webhook-subscriptions/{webhook_subscription_id} et { "status": "enabled" } reprend les livraisons à partir de ce moment. Les événements survenus pendant la désactivation ne sont pas renvoyés : rattrapez-les avec GET /events.

Quand le titulaire du compte déconnecte votre application, ses abonnements s'arrêtent et affichent disabled_reason: connection_ended. Une fois que le titulaire du compte a connecté votre application de nouveau, listez vos abonnements, puis créez ou réactivez ceux dont vous avez besoin.

Ordre et doublons

Les événements peuvent arriver dans le désordre et plus d'une fois. Écrivez votre gestionnaire de façon que cela n'ait pas d'importance.

  • Ignorez les doublons grâce à id. Conservez les identifiants que vous avez traités pendant au moins les 3 jours au cours desquels Matchai continue de réessayer.
  • Déterminez ce qui est à jour avec created_at, ou redemandez. Un événement plus ancien sur le même objet peut arriver après un plus récent. Comparez created_at avec ce que vous avez, ou relisez l'objet avec un GET.
apply-event.ts
type StoredObject = { lastEventAt: number };

// Your own storage, keyed by the object's id.
const store = new Map<string, StoredObject>();

/** Returns true when the event is newer than what you hold, so you should apply it. */
export function isNewer(objectId: string, eventCreatedAt: string): boolean {
  const eventAt = Date.parse(eventCreatedAt);
  const current = store.get(objectId);
  if (current && current.lastEventAt >= eventAt) return false; // a late, older event
  store.set(objectId, { lastEventAt: eventAt });
  return true;
}

Pour tout ce qui doit être parfaitement exact, traitez l'événement comme un signal et lisez l'objet dans l'API. data.object est aussi offert à GET /events/{event_id}.

Rattraper le retard après une interruption

GET /events conserve 30 jours d'événements pour la connexion, les plus récents en premier, sous la même forme que les livraisons. Utilisez-le après une panne, après avoir réactivé un abonnement, ou à intervalles réguliers comme filet de sécurité. Il exige webhooks:manage.

Champ

Type

catch-up.ts
const api = 'https://api.matchai.co/v1';

export async function eventsSince(since: string, token: string) {
  const events: Array<{ id: string; type: string; created_at: string }> = [];
  let cursor: string | null = null;

  do {
    const url = new URL(`${api}/events`);
    url.searchParams.set('since', since);
    url.searchParams.set('limit', '100');
    if (cursor) url.searchParams.set('cursor', cursor);

    const response = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
    if (!response.ok) throw new Error(`Matchai answered ${response.status}`);

    const page = await response.json();
    events.push(...page.data);
    cursor = page.next_cursor;
  } while (cursor);

  // The list is newest first. Replay oldest first, skipping ids you already handled.
  return events.reverse();
}

Tester votre point de terminaison

POST /webhook-subscriptions/{webhook_subscription_id}/test envoie immédiatement un événement webhook.test au point de terminaison et renvoie la livraison. L'événement a test_mode: true, et data.object.message est un texte fixe. Renvoyez un 2xx pour réussir le test.

curl -X POST https://api.matchai.co/v1/webhook-subscriptions/8b9c0d1e-2f3a-4b4c-9d5e-6f7a8b9c0d1e/test \
  -H "Authorization: Bearer $MATCHAI_API_KEY" \
  -H "Idempotency-Key: 2c7d1a40-6e3b-4f8a-b9d2-0a1b2c3d4e5f"
{
  "id": "9c0d1e2f-3a4b-4c5d-8e6f-7a8b9c0d1e2f",
  "event_id": "0d1e2f3a-4b5c-4d6e-9f7a-8b9c0d1e2f3a",
  "event_type": "webhook.test",
  "attempt": 1,
  "status": "succeeded",
  "response_status": 200,
  "error": null,
  "created_at": "2026-10-16T19:12:46Z",
  "next_attempt_at": null
}

D'autres façons de tester :

  • Utilisez le mode test. Une application connectée seulement au compte Matchai de son propre développeur est en mode test et n'est pas facturée. Ses événements portent test_mode: true, alors vous pouvez d'abord construire et vérifier tout le parcours avec votre propre compte.
  • Rendez votre machine joignable. Votre point de terminaison doit être en https. Pendant le développement, utilisez un tunnel qui donne à votre serveur local une adresse https publique, et abonnez cette adresse.
  • Rejouez un véritable événement. Lisez-le avec GET /events/{event_id}, ou renvoyez une livraison échouée avec POST /webhook-subscriptions/{webhook_subscription_id}/deliveries/{delivery_id}/retry.

Renouveler le secret de signature

Renouvelez un secret à intervalles réguliers, ou sur-le-champ s'il a pu être divulgué. Le renouvellement ne fait jamais perdre d'événement, car l'ancien secret continue de fonctionner pendant 24 heures.

Appelez POST /webhook-subscriptions/{webhook_subscription_id}/rotate-secret. La réponse est l'abonnement avec un nouveau secret, affiché une seule fois.

Pendant les 24 heures suivantes, chaque livraison porte une valeur v1 par secret dans Matchai-Signature.

Déployez le nouveau secret. Le code de vérification ci-dessus accepte une livraison dès qu'une valeur v1 correspond : il fonctionne donc avec l'ancien secret avant le déploiement et avec le nouveau après.

Après 24 heures, seul le nouveau secret signe les livraisons.

Journaux de livraison

GET /webhook-subscriptions/{webhook_subscription_id}/deliveries liste les tentatives de livraison récentes pour un point de terminaison, les plus récentes en premier. Elles sont conservées pendant 30 jours. Utilisez status (pending, succeeded ou failed) pour restreindre la liste. Il exige webhooks:manage.

{
  "data": [
    {
      "id": "9c0d1e2f-3a4b-4c5d-8e6f-7a8b9c0d1e2f",
      "event_id": "0d1e2f3a-4b5c-4d6e-9f7a-8b9c0d1e2f3a",
      "event_type": "lead.created",
      "attempt": 2,
      "status": "failed",
      "response_status": 503,
      "error": null,
      "created_at": "2026-10-16T19:13:46Z",
      "next_attempt_at": "2026-10-16T19:18:46Z"
    }
  ],
  "next_cursor": null
}

Champ

Type

Pour renvoyer immédiatement une livraison échouée, appelez POST /webhook-subscriptions/{webhook_subscription_id}/deliveries/{delivery_id}/retry. L'appel renvoie une nouvelle livraison.

Actions finales et approbations

Quand votre connexion est réglée sur Me demander, une action finale, comme envoyer un message ou publier une inscription, renvoie 202 Accepted avec un approval_id. Rien ne se passe tant que le titulaire du compte ne l'a pas approuvée dans Matchai.

Matchai envoie ensuite approval.resolved à votre application, quelles que soient ses portées. data.object.id est l'approval_id du 202. status indique si l'action a été approuvée, si elle a été refusée ou si elle a expiré. Si elle a été approuvée, result.status_code et result.body sont ce que l'appel a renvoyé quand Matchai l'a effectué.

approvals.ts
type Approval = {
  id: string;
  status: 'pending' | 'approved' | 'declined' | 'expired' | 'cancelled';
  summary: string;
  result: { status_code: number; body: Record<string, unknown> } | null;
};

export function onApprovalResolved(approval: Approval): void {
  if (approval.status === 'approved') {
    console.log('Done:', approval.summary, approval.result?.status_code);
  } else {
    console.log('Not done:', approval.summary, approval.status);
  }
}

Si vous ne pouvez pas recevoir de webhooks, interrogez plutôt GET /approvals/{approval_id} à intervalles réguliers. Consultez getApproval.

Avant la mise en production

  • Votre point de terminaison est en https et répond un 2xx en moins de 10 secondes.
  • Vous vérifiez Matchai-Signature sur le corps brut, vous comparez en temps constant et vous refusez un horodatage décalé de plus de 5 minutes.
  • Vous ignorez les doublons grâce à id, et vous gérez les événements qui arrivent dans le désordre.
  • Vous ignorez les types d'événements et les champs que vous ne connaissez pas.
  • Le secret de signature est dans un coffre de secrets, et vous savez comment le renouveler.
  • Un appel planifié à GET /events comble les éventuelles lacunes.

Sur cette page