Aller au contenu

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:// ou http:// (mais de préférence https://). 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 :

TypeDéclenché quand
comment.pendingUn nouveau commentaire attend la modération.
comment.approvedUn admin approuve un commentaire.
comment.rejectedUn commentaire est rejeté.
umbrella.createdUn élément de roadmap est créé (brouillon ou publié).
umbrella.movedUn élément de roadmap change de colonne de statut.
umbrella.shippedLa colonne d'arrivée est « livré » ou « terminé ». Déclenché en plus de umbrella.moved.
umbrella.publishedUn élément de roadmap devient public pour la première fois (visible dans l'API publique).
umbrella.unpublishedUn élément de roadmap est retiré de la roadmap publique.
release.publishedUne version est publiée.
release.unpublishedUne version est retirée.
follow_up.askedUn admin envoie une question de suivi.
follow_up.answeredLa personne répond à une question de suivi.
feedback.receivedUn 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.

json
{
  "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.

json
{
  "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.