Webhooks
Plutôt que d'interroger l'API en boucle pour savoir si une commande a bougé, laissez la plateforme appeler votre serveur au moment où elle bouge.
Enregistrer une adresse
Dans Applications → API, ajoutez l'adresse HTTPS de votre serveur et choisissez les événements qui vous intéressent. Un secret de signature vous est donné à ce moment-là, et une seule fois.
Événements
lead.created | Un lead est entré (saisie, feuille, boutique ou API). |
|---|---|
lead.confirmed | Un agent a confirmé la commande avec le client. |
lead.canceled | Le lead est perdu (annulé, faux numéro, doublon, fantaisiste). |
order.shipped | Le colis est parti chez le livreur. |
order.delivered | Le colis est livré et encaissé côté livreur. |
order.returned | Le colis revient (refus ou client injoignable). |
order.paid | La commande est payée au vendeur (écriture au grand livre). |
Vous pouvez viser une famille entière : order.*. Sans précision, tous les
événements sont envoyés.
Ce que vous recevez
Un POST en JSON, avec ces en-têtes :
X-CODFamilia-Event | Le code de l'événement. |
|---|---|
X-CODFamilia-Delivery | Identifiant de l'envoi — le même en cas de réessai. |
X-CODFamilia-Timestamp | Horodatage Unix de l'envoi. |
X-CODFamilia-Signature | sha256=… (voir plus bas). |
{
"event": "order.delivered",
"sent_at": "2026-05-04T11:22:33+01:00",
"data": {
"id": 412,
"ref": "TAH-00042",
"external_ref": "CMD-1042",
"customer": { "name": "Yassine A.", "phone": "0612345678", "city": "Casablanca", "address": "12 rue des Orangers" },
"total_client": "299.00",
"seller_profit": "90.00",
"confirmation_status": "confirmed",
"shipping_status": "delivered",
"payment_status": "unpaid",
"tracking_number": "OZ123456",
"items": [{ "sku": "CF-TEETH-03", "name": "Stylo blanchiment dentaire", "quantity": 1 }]
}
}
Vérifier la signature
Une adresse « secrète » ne prouve rien : quiconque la découvre peut vous envoyer un faux
« commande livrée ». Vérifiez donc la signature avant de traiter l'événement. Elle est calculée sur
horodatage . "." . corps brut — le corps tel qu'il arrive, avant tout décodage.
<?php
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_CODFAMILIA_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_CODFAMILIA_SIGNATURE'] ?? '';
$mine = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, 'whsec_votre_secret');
if (!hash_equals($mine, $sig) || abs(time() - (int) $ts) > 300) {
http_response_code(400); // signature invalide, ou envoi trop ancien (rejeu)
exit;
}
$event = json_decode($body, true);
// … traitez $event['data'], puis répondez 200 le plus vite possible
http_response_code(200);
# Node.js
const crypto = require('crypto');
const mine = 'sha256=' + crypto.createHmac('sha256', SECRET).update(ts + '.' + rawBody).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(mine), Buffer.from(sig))) return res.sendStatus(400);
Réessais
Un envoi est réussi si votre serveur répond un code 2xx. Sinon il est réessayé, de plus en
plus espacé (1, 5, 25, 125 minutes, puis toutes les 4 heures), jusqu'à 6 tentatives. Un serveur éteint une nuit retrouve donc ses événements au matin ; un serveur définitivement
disparu n'est pas appelé indéfiniment. Après 25 échecs consécutifs,
l'adresse est désactivée et vous en êtes averti : vous la réactivez d'un clic une fois votre serveur réparé.
200 ; faites le travail ensuite, dans votre propre file. Un même
événement peut arriver deux fois (un réessai après une réponse perdue) : utilisez
X-CODFamilia-Delivery pour l'ignorer la seconde fois.
Ce que vous ne recevrez jamais
Un webhook ne transporte que ce que le vendeur voit déjà dans son espace. Ni prix d'achat, ni marge de la plateforme, ni identité des agents : ce n'est pas une porte de service vers des données internes.