Webhooks
Les endpoints de webhooks entrants, la façon dont chacun vérifie son expéditeur, et comment l'idempotence est tenue fournisseur par fournisseur.
9 endpoints de webhooks. Tous entrants : ce sont des endpoints que BoostEcom expose pour recevoir des événements, pas des endpoints que vous enregistrez pour recevoir les nôtres.
| Endpoint | Expéditeur | Clé d'idempotence |
|---|---|---|
/api/webhooks/stripe | Stripe | StripeEvent par event.id |
/api/webhooks/shopify/events | Shopify | ShopifyWebhookEvent |
/api/webhooks/shopify/customer-redact | Shopify (RGPD, obligatoire) | ShopifyCustomerRedaction |
/api/webhooks/shopify/redact | Shopify (RGPD, obligatoire) | ShopifyComplianceRequest |
/api/webhooks/shopify/data-request | Shopify (RGPD, obligatoire) | ShopifyComplianceRequest |
/api/webhooks/resend | Resend | EmailDeliveryEvent.providerEventId |
/api/webhooks/github | GitHub (console de développement) | GitHubWebhookEvent par identifiant de livraison |
/api/webhooks/creative-render | GitHub Actions (rendus Remotion) | la ligne du rendu elle-même : sur une ligne déjà clôturée, l'appel est sans effet |
/api/webhooks/[platform] | Chat SDK / WhatsApp | propre à chaque plateforme |
Rendus créatifs
Endpoint interne, listé ici parce que nous l'exposons. Un rendu Remotion tourne sur un runner GitHub Actions. À la fin, l'exécution appelle cet endpoint pour clôturer la ligne du rendu : livrée avec son fichier, ou en échec avec la conclusion propre à l'exécution. L'appel est signé en HMAC-SHA256 sur le corps brut ; sans signature valide, il est refusé avec un 401.
L'endpoint clôture, il ne devine jamais. Il ne demande pas à GitHub de confirmer ce que l'exécution vient de lui dire, car une seconde source de vérité sur une même exécution diverge de la première. Une ligne déjà clôturée n'est plus touchée : une double livraison ne peut donc pas la rouvrir. Et si aucun callback n'arrive, un job horaire clôture la ligne en échec plutôt que de la laisser « en cours » indéfiniment. Une file qui ne se vide jamais est une file que plus personne ne lit.
Stripe
Chaque gestionnaire d'événement est idempotent sur event.id, enregistré
dans StripeEvent. Une nouvelle livraison du même événement est sans
effet, et c'est important : l'attribution des crédits et les changements
de plan passent par ici.
Au-delà des abonnements et des crédits, le gestionnaire traite aussi les
événements du marketplace : account.updated, charge.refunded et
payout.failed pour Stripe Connect.
L'attribution des crédits lit metadata.amount, le montant hors
taxes, jamais amount_total. Stripe Tax est actif, donc les deux
diffèrent.
RGPD Shopify : les trois endpoints obligatoires
Shopify en impose trois, et leur comportement diffère volontairement :
customers/redact lève une barrière ShopifyCustomerRedaction
avant de répondre 200. L'effacement proprement dit est pris en
charge par le cron gdpr-erasure : namespaces vectoriels, liens client,
lignes d'attribution, payloads d'audit archivés. completedAt fait foi :
une ligne qui n'en a pas est une obligation non remplie, et elle apparaît
comme telle.
shop/redact enregistre une ShopifyComplianceRequest que le même
cron traite : identifiants d'accès, namespaces vectoriels, payloads bruts
archivés dans AuditLog, texte libre des commandes. Il pose
completedAt et nomme dans pendingSteps ce qui attend encore un
opérateur.
customers/data_request est entièrement manuel. Seul un humain
pose handledAt. Au-delà de 30 jours, une ligne qui n'en a pas déclenche
un log de niveau error : le système refuse que l'obligation tombe dans
l'oubli.
L'idempotence de ces trois endpoints repose sur leurs deux tables
propres, pas sur ShopifyWebhookEvent.
Resend
Livraisons, rebonds et plaintes pour spam. Signature vérifiée via Svix,
idempotence sur EmailDeliveryEvent.providerEventId.
GitHub
Endpoint interne : il rapporte ce qu'il advient des sessions d'agents
lancées depuis la console opérateur (pull request ouverte, relue,
fusionnée), et c'est le seul chemin d'écriture qui fait avancer une ligne
de la feuille de route lors d'une fusion. Signature HMAC-SHA256 sur le
corps brut (X-Hub-Signature-256), idempotence sur X-GitHub-Delivery.
Si le secret n'est pas défini, toutes les livraisons sont refusées.
Vérification
Chaque endpoint vérifie son expéditeur avant tout traitement : signature Stripe, HMAC Shopify, Svix pour Resend. Un payload non vérifié est rejeté. Il n'est jamais traité en supposant qu'il vient sans doute du bon endroit.
Webhooks sortants
Il existe une seule surface sortante, absente du tableau ci-dessus puisqu'il ne s'agit pas d'un endpoint entrant : les webhooks de statut. Les événements d'incident sont signés en HMAC-SHA256 et envoyés vers des URL configurées depuis le panneau de statut de l'administration.