Webhooks
Nur ausgehend. Opini sendet einen POST mit JSON-Body an eine URL, die dir gehört, sobald etwas Wichtiges passiert: Ein Roadmap-Eintrag? wird ausgeliefert, ein Kommentar wartet, ein Release erscheint, eine Rückfrage wird beantwortet. Füg eine Discord-Webhook-URL ein, und Nachrichten landen ohne eine Zeile Code in deinem Kanal.
Was sie sind
Drei häufige Wünsche, die Webhooks direkt erfüllen:
- „Sag meinem Discord-Kanal Bescheid, wenn ein neuer Kommentar auf Freigabe wartet.“
- „Starte meine Deploy-Pipeline, wenn wir ein Release veröffentlichen.“
- „Benachrichtige den Support-Kanal, wenn eine Rückfrage beantwortet wird.“
Immer derselbe Baustein: Opini, ruf diese URL auf, wenn X passiert, mit JSON, das ich lesen kann. Webhooks gelten pro Projekt, höchstens 10 pro Projekt.
Einen anlegen
Öffne im Dashboard Einstellungen → Webhooks. Füll aus:
- URL:
https://oderhttp://(aber bittehttps://). Keine Zugangsdaten in der URL. - Name: etwas Lesbares, z. B. ops-discord oder deploy-pipeline. Erscheint in der Webhook-Liste und im Zustellungsverlauf.
- Events: wähl die gewünschten Arten, gruppiert nach Eintrag (Kommentare, Roadmap-Einträge, Releases, Rückfragen, Feedback).
- Discord-kompatibel: einschalten, wenn die URL ein Discord-Kanal-Webhook ist. Siehe unten.
- Signatur-Geheimnis: (optional) erzeug eines, um Anfragen mit HMAC-SHA256 zu signieren. Wird beim Anlegen einmal gezeigt; bei Verlust neu erzeugen.
Ein Button Test-Event senden in jeder Zeile schickt ein Beispiel-Event an die URL, damit du prüfen kannst, ob sie erreichbar ist, bevor du dich darauf verlässt.
Events
Erste Liste; unbekannte Arten bekommen beim Anlegen ein 422:
| Art | Wird ausgelöst, wenn |
|---|---|
| comment.pending | Ein neuer Kommentar auf Moderation wartet. |
| comment.approved | Ein Admin einen Kommentar freigibt. |
| comment.rejected | Ein Kommentar abgelehnt wird. |
| umbrella.created | Ein Roadmap-Eintrag angelegt wird (Entwurf oder veröffentlicht). |
| umbrella.moved | Ein Roadmap-Eintrag die Statusspalte wechselt. |
| umbrella.shipped | Die Zielspalte „ausgeliefert“ oder „erledigt“ ist. Zusätzlich zu umbrella.moved. |
| umbrella.published | Ein Roadmap-Eintrag zum ersten Mal öffentlich wird (erscheint in der öffentlichen Roadmap-API). |
| umbrella.unpublished | Ein Roadmap-Eintrag von der öffentlichen Roadmap genommen wird. |
| release.published | Ein Release veröffentlicht wird. |
| release.unpublished | Ein Release offline genommen wird. |
| follow_up.asked | Ein Admin eine Rückfrage sendet. |
| follow_up.answered | Die Person auf eine Rückfrage antwortet. |
| feedback.received | Neues Feedback in der Triage landet (Widget oder API). |
Webhooks sehen nur Events nach ihrer Erstellung, nichts rückwirkend. Das ist Absicht: Monate an Änderungen an eine frisch abonnierte URL nachzuspielen, will fast nie jemand.
Aufbau der Daten
Der Rohmodus ist Standard. Jede Anfrage hat denselben Umschlag; das Objekt data hängt von der Event-Art ab.
{
"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"
}
}Discord-kompatibler Modus
Schalte Discord-kompatibel ein, und die Daten werden zu {content, embeds}, dem Format, das Discords Webhook-API erwartet. Keine Middleware nötig.
{
"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
}]
}Die Farben hängen von der Art ab: wartende Kommentare bernstein, Freigaben grün, ausgelieferte Einträge violett, alles andere im Markenblau. Beschreibungen sind auf 300 Zeichen begrenzt (Discord erlaubt 4096, wir bleiben kurz, damit das Embed überschaubar bleibt).
Signatur und Header
Wenn du ein Signatur-Geheimnis einrichtest, trägt jede Anfrage:
X-Opini-Signature: sha256=<hex>: HMAC-SHA256 des rohen Bodys, mit dem Geheimnis als Schlüssel.X-Opini-Event: <event_kind>: die Event-Art, für schnelles Routing.X-Opini-Delivery: <delivery_id>: eine eindeutige ID pro Zustellversuch. Nutz sie, um Wiederholungen beim Empfänger zu erkennen.
Discord prüft diese Header nicht, sie stören bei einem Discord-kompatiblen Webhook also nicht. Ein neues Geheimnis erzeugt einen neuen Wert; der alte gilt sofort nicht mehr.
Zustellungsverlauf
Klick auf eine Webhook-Zeile, und ein Seitenbereich zeigt die letzten 100 Zustellversuche. Jede Zeile zeigt Event-Art, Status (2xx grün, 4xx bernstein, 5xx oder Netzwerkfehler rot), Dauer und wie lange es her ist. Aufgeklappt siehst du eine Zusammenfassung von Anfrage und Antwort.
Versuche jenseits der letzten 100 werden automatisch gelöscht: Das ist kein vollständiges Audit-Log, sondern ein Blick auf „was in den letzten Stunden passiert ist“.
Wiederholungen und Limits
- 3 Versuche, exponentiell verzögert. 1 s → 4 s → 16 s. Nach drei Versuchen vermerkt Opini die fehlgeschlagene Zustellung und macht weiter.
- 5 Sekunden Timeout pro Anfrage. Ein langsamer Endpunkt zählt als Fehler.
- 10 Webhooks pro Projekt. Brauchst du mehr, fass zusammen: Die meisten Teams landen bei Chat, Pipeline und einem Debug-Endpunkt.
- Deaktivierte Webhooks werden übersprungen. Schalte einen Webhook beim Debuggen lieber aus, statt ihn zu löschen: Der Zustellungsverlauf bleibt.
Webhooks feuern bei Änderungen an Kommentaren, Roadmap-Einträgen, Releases und Rückfragen.