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.
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.
| Pour | Appel | Référence |
|---|---|---|
| Lister vos abonnements | GET /webhook-subscriptions | listWebhookSubscriptions |
| 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'abonnement | PATCH /webhook-subscriptions/{webhook_subscription_id} | updateWebhookSubscription |
| Cesser d'envoyer des événements à un point de terminaison | DELETE /webhook-subscriptions/{webhook_subscription_id} | deleteWebhookSubscription |
| Émettre un nouveau secret de signature | POST /webhook-subscriptions/{webhook_subscription_id}/rotate-secret | rotateWebhookSecret |
| Envoyer un événement de test maintenant | POST /webhook-subscriptions/{webhook_subscription_id}/test | testWebhookSubscription |
| Voir les tentatives de livraison | GET /webhook-subscriptions/{webhook_subscription_id}/deliveries | listWebhookDeliveries |
| Renvoyer une livraison échouée | POST /webhook-subscriptions/{webhook_subscription_id}/deliveries/{delivery_id}/retry | retryWebhookDelivery |
| Rattraper les événements passés | GET /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énement | Envoyé quand | data.object | Portée requise |
|---|---|---|---|
lead.created | Un 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 contact | contacts:read |
contact.created | Tout nouveau contact, quel que soit son type. Un nouveau prospect envoie aussi lead.created. | Le contact | contacts:read |
contact.updated | Les 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 contact | contacts:read |
contact.converted | Un prospect est devenu client, ou un client est devenu professionnel. data.previous.type contient l'ancien type. | Le contact | contacts:read |
contact.deleted | Un contact a été supprimé. | Un objet supprimé | contacts:read |
Conversations et messages
| Événement | Envoyé quand | data.object | Portée requise |
|---|---|---|---|
conversation.created | Une nouvelle conversation dont le compte fait partie. | La conversation | messages:read |
message.received | Quelqu'un d'autre a écrit dans une conversation dont le compte fait partie. | Le message | messages:read |
message.sent | Le compte a envoyé un message, depuis l'application, l'Assistant Matchai ou une application comme la vôtre. | Le message | messages:read |
Rendez-vous
| Événement | Envoyé quand | data.object | Portée requise |
|---|---|---|---|
appointment.requested | Quelqu'un a demandé un moment au titulaire du compte. La demande attend sa réponse. | Le rendez-vous | calendar:read |
appointment.booked | Un rendez-vous est confirmé : il a été réservé directement, ou une demande a été acceptée. | Le rendez-vous | calendar:read |
appointment.rescheduled | Un rendez-vous a une nouvelle heure, ou une nouvelle heure a été proposée (status: reschedule_pending). | Le rendez-vous | calendar:read |
appointment.cancelled | Un rendez-vous a été annulé, ou une demande a été refusée. | Le rendez-vous | calendar:read |
Inscriptions
| Événement | Envoyé quand | data.object | Portée requise |
|---|---|---|---|
listing.created | Une nouvelle inscription en brouillon, créée à la main, par la création par IA ou par importation. | L'inscription | listings:read |
listing.updated | Les 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'inscription | listings:read |
listing.published | Une inscription a été mise en ligne sur le Web public. | L'inscription | listings:read |
listing.unpublished | Une inscription est redevenue un brouillon. | L'inscription | listings:read |
listing.archived | Une inscription a été retirée du marché. | L'inscription | listings:read |
listing.deleted | Une inscription a été supprimée. | Un objet supprimé | listings:read |
listing.saved | Quelqu'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_at | listings:read et contacts:read |
listing.generation.completed | Une création d'inscription par IA a réussi ou échoué. | La génération d'inscription | listings:read |
Documents, invitations et notifications
| Événement | Envoyé quand | data.object | Portée requise |
|---|---|---|---|
document.created | Un document a été ajouté à Matchai Cloud, par téléversement, par courriel ou au moyen d'un lien de dépôt. | Le document | documents:read |
drop_link.upload_received | Quelqu'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 document | documents:read |
invitation.accepted | Quelqu'un a accepté une invitation envoyée par le compte. | L'invitation | teams:read ou projects:read |
notification.created | Le compte a reçu une notification. | La notification | notifications:read |
Approbations et tests
| Événement | Envoyé quand | data.object | Portée requise |
|---|---|---|---|
approval.resolved | Une 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'approbation | Aucune. L'événement va à l'application qui a fait la demande. |
webhook.test | Vous l'avez envoyé avec POST /webhook-subscriptions/{webhook_subscription_id}/test. | Un message fixe | webhooks: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ête | Signification |
|---|---|
Matchai-Signature | La signature à vérifier. Elle ressemble à t=1792177965,v1=5257a869…. Pendant un renouvellement du secret, elle porte deux valeurs v1. |
Matchai-Event-Id | La même valeur que l'id du corps. Servez-vous-en pour ignorer les doublons. |
Matchai-Delivery-Attempt | Le 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=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdPrenez 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_.
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.
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.
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.
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. Comparezcreated_atavec ce que vous avez, ou relisez l'objet avec unGET.
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
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 adressehttpspublique, et abonnez cette adresse. - Rejouez un véritable événement. Lisez-le avec
GET /events/{event_id}, ou renvoyez une livraison échouée avecPOST /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é.
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
httpset répond un2xxen moins de 10 secondes. - Vous vérifiez
Matchai-Signaturesur 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 /eventscomble les éventuelles lacunes.
Créateurs de sites Web
Utilisez les webhooks avec un flux d'inscriptions, la réception des prospects et les demandes de visite.
Se connecter avec Matchai
Comment une application obtient une connexion au compte d'un professionnel.
Erreurs
Les codes d'erreur et la façon de les traiter.
Limites de débit
Les limites par connexion, et que faire en cas de 429.
Pages lisibles par l'IA
Comment chaque site Matchai public se décrit aux assistants IA et aux robots, de llms.txt aux copies Markdown et au JSON-LD. À reproduire sur votre site.
Créateurs de sites Web
Affichez les inscriptions Matchai d'un courtier sur vos sites, dès maintenant et sans clé. Trousse partenaire à venir : flux, prospects, visites et webhooks.