Docs

Dépannage des webhooks

Résous un événement de test ou une livraison en échec par symptôme, avec les messages exacts que GrowWriter affiche, ce que chacun veut dire et quoi faire.

Dernière mise à jour le

Chaque entrée ci-dessous commence par ce que tu vois, dit ce que ça veut dire en mots simples et finit par la solution. Commence par la première entrée : presque tous les problèmes de première connexion se résolvent en trouvant le vrai code de statut.

D'abord, trouve le vrai code de statut

À faire. Envoie une requête de test non signée à ton endpoint depuis ton propre ordinateur et regarde seulement la ligne de statut et le header Location :

Vérifier ce que ton endpoint répond vraiment
curl -sS -o /dev/null -D - -X POST \
  -H "Content-Type: application/json" -d '{}' \
  https://your-site.com/webhooks/growwriter | grep -iE "^(HTTP|Location)"

Comment lire le résultat. La requête n'est pas signée, un 401 est donc la bonne réponse : ton endpoint est en ligne et rejette les requêtes non signées, exactement comme il le doit.

Tu obtiensCe que ça veut dire
HTTP … 401 ou 400Bien. L'endpoint tourne et vérifie les signatures. Les requêtes signées marcheront.
HTTP … 3xx + Location:Ton URL redirige. Utilise l'adresse dans Location: à la place. Vois redirections.
HTTP … 404Mauvais chemin, ou la route n'est pas déployée.
HTTP … 405La route existe mais n'accepte pas POST.
HTTP … 500Ton code a planté. En général une variable d'environnement manquante (le secret).
Erreur de connexion / timeoutDNS, pare-feu, ou le site n'est pas public.

Si tu obtiens un 3xx, relance la commande avec l'adresse de Location: jusqu'à obtenir un 4xx. Cette adresse finale est celle à saisir dans GrowWriter.

L'événement de test échoue avec un HTTP 301, 302, 303, 307 ou 308

Ce que tu vois. La boîte de dialogue affiche L'URL de ton endpoint redirige. Saisis l'adresse vers laquelle elle redirige et réessaie, avec la cible de la redirection sur la ligne du dessous.

Ce que ça veut dire. Ton site a répondu « va plutôt là-bas ». Les navigateurs suivent ça en silence, ton site semble donc fonctionner quand tu l'ouvres. GrowWriter ne suit jamais une redirection : la suivre pourrait perdre le corps de la requête ou casser la signature. Une redirection compte donc toujours comme une livraison en échec, et les trois tentatives vont à l'URL exacte que tu as saisie.

Les causes habituelles :

CauseRedirection typique
example.com contre www.example.comTon hébergeur redirige l'un vers l'autre. Utilise celui vers lequel il redirige.
http:// au lieu de https://http://… → https://…
Un slash final que ton framework ajoute ou retire/webhook → /webhook/ ou l'inverse
Un préfixe de langue que ton framework ajoute/api/webhook → /fr/api/webhook
Une URL de prévisualisation réassignée*.vercel.app → ton domaine de production
Une règle de CDN ou de proxy (« Always use HTTPS », page rules)n'importe laquelle des précédentes

Solution. Copie l'adresse depuis l'erreur (ou depuis le header Location: de la vérification ci-dessus) et saisis-la comme URL de l'endpoint. Si la boîte de dialogue de connexion est encore ouverte, clique sur Retour ; si tu l'as déjà fermée, clique sur Modifier l'URL sur la ligne du webhook de la page Canaux. Les deux conservent ton secret de signature. Déconnecter puis reconnecter crée à la place un nouveau secret, que tu devrais mettre à jour sur ton serveur.

« Ton endpoint n'a pas répondu à temps »

Ce que ça veut dire. GrowWriter a attendu une réponse pendant 10 secondes et a abandonné. Chacune des trois tentatives a ses propres 10 secondes.

Solution. Réponds avant de faire un travail lent. Le guide décrit deux schémas sûrs : garde un handler qui peut tourner deux fois sans danger et laisse un réessai finir le travail, ou réponds 200 d'abord et fais le travail lourd dans une tâche de fond. Vois Et la limite des 10 secondes ?.

« Ton endpoint a rejeté la livraison »

Ce que ça veut dire. Ton endpoint a répondu avec un statut qui n'est pas 2xx. Le message cite ce statut et les 200 premiers caractères du corps de ta réponse, renvoie donc une courte raison en texte brut depuis ton handler et tu la liras directement dans la boîte de dialogue. Le statut te dit où regarder :

  • 401 ou 403 : ta vérification de signature a dit non. Vois la signature ne correspond jamais.
  • 404 : mauvais chemin. Compare l'URL dans GrowWriter avec ta route.
  • 405 : la route n'accepte pas POST.
  • 413 : ton serveur refuse des corps de cette taille. Un article complet peut peser plusieurs centaines de kilo-octets. Vois payloads volumineux.
  • 500 : ton code a planté. Regarde les logs de ton serveur ; une variable d'environnement de secret manquante est la cause la plus fréquente.

« Voir l'article » manque ou ouvre la mauvaise page

Ce que ça veut dire. Le lien est l'URL que ton endpoint a renvoyée dans le corps de sa réponse 2xx, sous la forme {"url": "https://…"}. Réponds n'importe quoi d'autre et il n'y a pas de lien. GrowWriter n'a aucun autre moyen de savoir où ton site a mis le billet.

Solution. Renvoie l'URL publique du billet depuis ton handler une fois le billet enregistré. Ce doit être une URL absolue en http ou https ; les chemins relatifs et les autres schémas sont ignorés. Vois La réponse pour les règles. Deux choses à savoir :

  • Si tu réponds 200 avant de créer le billet (le schéma de la tâche de fond), tu ne peux pas encore connaître son URL, la boîte de dialogue n'affiche donc aucun lien.
  • Le lien stocké est remplacé à chaque livraison réussie. Si un lien pointe vers une ancienne adresse, publie l'article à nouveau et la réponse actuelle de ton endpoint le remplace.

« Échec de la livraison après plusieurs tentatives »

Ce que ça veut dire. GrowWriter n'a pas pu se connecter du tout à ton endpoint en trois tentatives : le DNS a échoué, la connexion a été refusée, ou le site n'est pas accessible depuis internet.

Solution. Vérifie que l'URL est en ligne, utilise https:// et est publique. Les adresses de réseaux privés et localhost sont bloquées pour des raisons de sécurité. Pour recevoir de vrais événements sur ta machine pendant le développement, utilise un service de tunnel (ngrok, cloudflared) et saisis l'adresse HTTPS du tunnel.

L'URL est refusée quand je connecte

GrowWriter vérifie l'URL avant de l'enregistrer. Les trois messages et leur sens :

  • L'URL du webhook doit utiliser HTTPS : seul https:// est accepté.
  • L'URL du webhook pointe vers une adresse privée et ne peut pas être utilisée : le nom d'hôte se résout en une adresse de réseau local ou privé (localhost, 10.x, 192.168.x et similaires). Utilise une adresse publique ou un tunnel.
  • L'URL du webhook est invalide : l'URL ne peut pas être parsée, contient un nom d'utilisateur ou un mot de passe, ou son nom d'hôte ne peut pas être résolu.

GrowWriter ne teste pas si l'endpoint répond à ce stade. C'est à ça que sert Envoyer un événement de test.

La signature ne correspond jamais

Parcours cette liste dans l'ordre. Chaque point explique toute une classe d'échecs :

  1. Vérifie sur le corps brut. Calcule la signature sur les octets exacts que tu as reçus, avant tout parsing JSON. Parser et re-sérialiser change les octets.
  2. Utilise le secret entier. La clé est la chaîne complète, préfixe whsec_ inclus.
  3. Vérifie la variable d'environnement. Même nom dans ton code et chez ton hébergeur, définie pour l'environnement qui sert l'endpoint, et redéployée après l'avoir définie.
  4. Vérifie la fenêtre de cinq minutes. Si l'horloge de ton serveur est décalée de plus de cinq minutes, chaque signature semble expirée. Synchronise l'horloge.
  5. Compare le format du header. X-GrowWriter-Signature est t=<secondes unix>,v1=<64 caractères hex>, et la chaîne signée est "<t>.<corps brut>".

Le test en local signe une requête de la même façon que GrowWriter, tu peux donc confirmer ta vérification sans attendre une vraie livraison.

Le test réussit mais les vrais articles échouent

Ce que ça veut dire. Le ping est minuscule. Un vrai article porte son contenu trois fois (markdown, HTML et blocs) et pèse en général quelques centaines de kilo-octets ; les articles longs peuvent dépasser 1 Mo. Les serveurs et frameworks plafonnent souvent les corps JSON bien en dessous.

Solution. Augmente la limite de taille du corps sur la route (par exemple express.raw({ type: 'application/json', limit: '10mb' }) ; la valeur par défaut d'Express est de 100 Ko), et assure-toi que ta logique d'enregistrement gère le payload complet, pas seulement les champs que le test a utilisés.

Le test réussit, mais le premier article échoue avec un 500 sur Vercel ou Netlify

Ce que ça veut dire. Ton handler écrit un fichier à côté de ton code. Les hébergeurs serverless montent ce répertoire en lecture seule, l'écriture lève donc une erreur (EROFS) et l'endpoint répond 500. Le test réussit parce que le ping répond avant tout enregistrement.

Solution. Stocke l'article dans une base de données, une API de CMS ou un stockage d'objets plutôt que dans un fichier local. Les exemples Next.js et Express du guide loggent le billet exactement pour cette raison ; remplace la ligne de log par ton appel de stockage. Si tu as seulement besoin d'un fichier temporaire, /tmp est accessible en écriture mais se vide entre deux invocations.

J'ai perdu mon secret de signature

Ce que ça veut dire. Le secret s'affiche une seule fois, dans la boîte de dialogue de connexion, et il est stocké chiffré. GrowWriter ne peut pas le réafficher.

Solution. Déconnecte le webhook sur la page Canaux et reconnecte-le. Ça crée un nouveau secret et retire l'ancien : mets à jour la variable d'environnement sur ton serveur, redéploie, puis envoie un événement de test.

L'article est en ligne sur mon site, mais l'admin de mon CMS affiche une erreur de validation

Ce que tu vois. Quelque chose comme Key on object value "growwriterId" is not allowed, ou une valeur de date refusée.

Ce que ça veut dire. Ton endpoint a enregistré un champ que ton CMS ne déclare pas (en général l'id GrowWriter), ou une valeur dans un format que le champ n'accepte pas (en général un timestamp complet dans un champ de date sans heure).

Solution. Ajoute un champ dédié pour l'id à ton modèle de contenu, et convertis les dates au format que ton CMS attend. Les deux sont expliqués dans Enregistre l'article dans ton CMS.

Un billet est repassé en brouillon en silence

Ce que ça veut dire. Le payload n'a pas d'indicateur publié. Si ton CMS traite un champ brouillon manquant comme « brouillon », le billet n'est publié que jusqu'à ce que quelqu'un l'enregistre dans l'admin.

Solution. Définis toi-même l'état publié, explicitement, quand tu traites article.published et article.updated. Vois la règle 3.

Mon build échoue sur { ou < dans l'article

Ce que ça veut dire. Le texte de l'article contient des caractères ordinaires comme {, } et <. Si tu compiles le markdown via MDX, JSX ou un moteur de templates, ces caractères sont lus comme du code.

Solution. Stocke le markdown comme donnée et rends-le avec un renderer Markdown, ou échappe ces caractères avant de compiler. content.html est déjà échappé et sûr à insérer directement. Vois la règle 6.

Les images sont énormes, manquantes, ou leurs URLs ont changé

  • Énormes : les images arrivent à leur résolution d'origine et peuvent dépasser 10 Mo. Redimensionne-les et compresse-les quand tu les télécharges.
  • Manquantes : coverImage est absent quand l'article n'a pas de couverture ; images est un tableau vide quand il n'y a pas d'image dans le texte. Vérifie la présence de la clé avant de la lire.
  • URLs changées : une image remplacée dans l'article reçoit une nouvelle URL à la livraison suivante. Les images inchangées gardent leurs URLs.

Les trois cas sont couverts dans la règle 5.

J'ai reçu le même article deux fois

  • Même id de livraison : un réessai. Ton endpoint a répondu tard ou avec une erreur à la première tentative. Saute le travail déjà fait pour cet id.
  • Id de livraison différent, article.published à nouveau : l'article a été publié vers une deuxième connexion webhook, ou la première connexion n'avait pas encore de livraison réussie. Fais un upsert sur article.id et la deuxième copie écrase la première.

J'ai supprimé un article dans GrowWriter, mais il est toujours sur mon site

Ce que ça veut dire. Supprimer un article (ou déconnecter le webhook) envoie article.unpublished avec l'id, le slug et la language de l'article, et rien d'autre. Si ton handler traite chaque événement autre que le ping comme un article à enregistrer, il a soit ignoré l'événement, soit tenté d'enregistrer un article sans contenu et répondu 500.

Solution. Traite article.unpublished avant l'enregistrement : retrouve le billet par article.id, puis masque-le ou supprime-le, selon ce que ton site préfère. Chaque exemple de récepteur du guide a cette branche. L'événement suit les règles de livraison normales (trois tentatives, puis rien), donc si ton endpoint était hors ligne à ce moment-là, le billet reste ; GrowWriter n'a pas de réessai ultérieur.

Le slug contient des caractères non latins, ou n'a pas changé avec le titre

Le slug est dérivé du titre une seule fois, au moment où le brief est généré. Les accents latins sont retirés (qué → que), mais les lettres des autres alphabets sont conservées, un titre japonais produit donc un slug japonais : encode-le en pourcentage avant de le mettre dans une URL. Il n'est jamais vide. Modifier le titre dans l'éditeur garde le slug, l'adresse du billet reste donc stable ; seul le choix d'un nouveau titre dans le writer produit un nouveau slug. N'utilise jamais le slug comme nom de fichier ni comme clé ; utilise article.id, et traite le slug comme une donnée d'affichage.

« Trop de requêtes. Réessaie plus tard »

Ce que ça veut dire. Ton compte a atteint une limite : 20 requêtes de publication par heure, ou 60 événements de test par heure. Rien n'est perdu. L'article reste dans ta Bibliothèque et la connexion reste telle qu'elle était.

Solution. Attends quelques minutes, puis publie ou envoie l'événement de test à nouveau. Si tu publies vers plusieurs canaux à la fois, chacun compte comme une requête.

La ligne du webhook affiche « Non vérifié »

Ce que ça veut dire. L'endpoint n'a encore jamais répondu à un événement de test ni à une publication. GrowWriter vérifie l'adresse quand tu le connectes, mais ne l'appelle pas tant que tu ne le fais pas.

Solution. Clique sur Envoyer un événement de test sur la ligne du webhook. Quand il réussit, la ligne passe à Connecté. S'il échoue, commence par la vérification du statut, puis redirections ou rejet. Publier vers un webhook Non vérifié est autorisé, et la première livraison réussie le vérifie aussi.

Toujours bloqué ?

Écris-nous depuis l'application en incluant : l'id de livraison (depuis le header X-GrowWriter-Delivery ou l'erreur), l'heure de la tentative, le statut que ton serveur a renvoyé, et la sortie de la vérification du statut. Avec ces quatre éléments, on peut en général répondre en un seul message.

Sur cette page