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 :
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 obtiens | Ce que ça veut dire |
|---|---|
HTTP … 401 ou 400 | Bien. 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 … 404 | Mauvais chemin, ou la route n'est pas déployée. |
HTTP … 405 | La route existe mais n'accepte pas POST. |
HTTP … 500 | Ton code a planté. En général une variable d'environnement manquante (le secret). |
| Erreur de connexion / timeout | DNS, 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 :
| Cause | Redirection typique |
|---|---|
example.com contre www.example.com | Ton 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 :
401ou403: 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 pasPOST.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
200avant 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.xet 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 :
- 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.
- Utilise le secret entier. La clé est la chaîne complète, préfixe
whsec_inclus. - 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.
- 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.
- Compare le format du header.
X-GrowWriter-Signatureestt=<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 :
coverImageest absent quand l'article n'a pas de couverture ;imagesest 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 surarticle.idet 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.