Webhooks
Uniquement sortants. Opini envoie un POST avec un corps JSON à une URL qui vous appartient dès qu'il se passe quelque chose d'important : un élément de roadmap? est livré, un commentaire attend, une version est publiée, une question de suivi reçoit une réponse. Collez l'URL d'un webhook Discord et les messages arrivent dans votre canal sans une ligne de code.
Ce que c'est
Trois demandes fréquentes auxquelles les webhooks répondent directement :
- « Préviens mon canal Discord quand un nouveau commentaire attend validation. »
- « Lance mon pipeline de déploiement quand on publie une version. »
- « Préviens le canal du support quand une question de suivi reçoit une réponse. »
C'est toujours la même brique : Opini, appelle cette URL quand X arrive, avec un JSON que je peux lire. Les webhooks sont par projet, 10 au maximum par projet.
En créer un
Ouvrez Paramètres → Webhooks dans le tableau de bord. Renseignez :
- URL:
https://ouhttp://(mais de préférencehttps://). Pas d'identifiants dans l'URL. - Nom: quelque chose de lisible, par exemple ops-discord ou pipeline-deploiement. Affiché dans la liste des webhooks et l'historique des envois.
- Événements: cochez les types voulus, groupés par élément (commentaires, éléments de roadmap, versions, questions de suivi, retours).
- Compatible Discord: activez-le si l'URL est un webhook de canal Discord. Voir plus bas.
- Secret de signature: (facultatif) générez-en un pour signer les requêtes en HMAC-SHA256. Affiché une fois à la création ; régénérez-le en cas de perte.
Un bouton Envoyer un événement de test sur chaque ligne envoie un événement fictif à l'URL, pour vérifier qu'elle répond avant de compter dessus.
Événements
Liste initiale ; les types inconnus reçoivent une 422 à la création :
| Type | Déclenché quand |
|---|---|
| comment.pending | Un nouveau commentaire attend la modération. |
| comment.approved | Un admin approuve un commentaire. |
| comment.rejected | Un commentaire est rejeté. |
| umbrella.created | Un élément de roadmap est créé (brouillon ou publié). |
| umbrella.moved | Un élément de roadmap change de colonne de statut. |
| umbrella.shipped | La colonne d'arrivée est « livré » ou « terminé ». Déclenché en plus de umbrella.moved. |
| umbrella.published | Un élément de roadmap devient public pour la première fois (visible dans l'API publique). |
| umbrella.unpublished | Un élément de roadmap est retiré de la roadmap publique. |
| release.published | Une version est publiée. |
| release.unpublished | Une version est retirée. |
| follow_up.asked | Un admin envoie une question de suivi. |
| follow_up.answered | La personne répond à une question de suivi. |
| feedback.received | Un nouveau retour arrive dans le tri (widget ou API). |
Les webhooks ne voient que les événements survenus après leur création, sans rattrapage. C'est voulu : rejouer des mois de changements vers une URL toute neuve n'est presque jamais ce qu'on veut.
Format du contenu
Le mode brut est celui par défaut. Chaque requête a la même enveloppe ; l'objet data dépend du type d'événement.
{
"event_id": "01JSAE...",
"event_kind": "comment.pending",
"occurred_at": "2026-04-24T07:03:11Z",
"project": { "id": "prj_...", "slug": "acme" },
"entity": {
"kind": "comment",
"id": "cmt_...",
"url": "https://opini.dev/app/orgs/<org>/projects/<project>/moderation"
},
"data": {
"body": "This is the comment body...",
"umbrella_id": "umb_...",
"umbrella_title": "Dark mode",
"author_name": "anon-dolphin"
}
}Mode compatible Discord
Activez Compatible Discord et le contenu devient {content, embeds}, le format attendu par l'API de webhooks de Discord. Aucun intermédiaire nécessaire.
{
"content": "New comment pending approval on **Dark mode**",
"embeds": [{
"title": "Dark mode",
"description": "This is the comment body, truncated to 300 chars...",
"url": "https://opini.dev/app/orgs/<org>/projects/<project>/moderation",
"color": 16730759
}]
}Les couleurs dépendent du type : commentaires en attente en ambre, validations en vert, éléments livrés en violet, le reste dans le bleu de la marque. Les descriptions sont limitées à 300 caractères (Discord en accepte 4096, mais nous restons courts pour que l'embed reste lisible).
Signature et en-têtes
Si vous configurez un secret de signature, chaque requête porte :
X-Opini-Signature: sha256=<hex>: le HMAC-SHA256 du corps brut, avec le secret comme clé.X-Opini-Event: <event_kind>: le type d'événement, pour router rapidement.X-Opini-Delivery: <delivery_id>: un identifiant unique par tentative d'envoi. Utilisez-le pour ignorer les doublons côté réception.
Discord ne vérifie pas ces en-têtes : ils ne gênent pas sur un webhook compatible Discord. Régénérer le secret produit une nouvelle valeur ; l'ancienne cesse aussitôt d'être valide.
Historique des envois
Cliquez sur une ligne de webhook : un panneau affiche les 100 dernières tentatives d'envoi. Chaque ligne montre le type d'événement, un statut (2xx vert, 4xx ambre, 5xx ou erreur réseau rouge), la durée et l'heure relative. En dépliant une ligne, vous voyez le résumé de la requête et de la réponse.
Au-delà des 100 plus récentes, les tentatives sont supprimées automatiquement : ce n'est pas un journal d'audit complet, mais un aperçu de « ce qui s'est passé ces dernières heures ».
Nouvelles tentatives et limites
- 3 tentatives, attente exponentielle. 1 s → 4 s → 16 s. Au-delà de trois, Opini enregistre l'échec et passe à la suite.
- 5 secondes maximum par requête. Un endpoint lent compte comme un échec.
- 10 webhooks par projet. S'il vous en faut plus, regroupez : la plupart des équipes finissent avec chat, pipeline et un endpoint de debug.
- Les webhooks désactivés sont ignorés. Désactivez un webhook plutôt que de le supprimer pendant le débogage : l'historique reste.
Les webhooks se déclenchent sur les changements des commentaires, des éléments de roadmap, des versions et des questions de suivi.