Webhooks

Webhooks sind automatisierte Benachrichtigungen an definierte URLs, die in Echtzeit über Ereignisse informieren. Sie sind nützlich, da sie eine sofortige Reaktion auf spezifische Ereignisse ermöglichen, ohne dass eine kontinuierliche Abfrage erforderlich ist. Webhooks tragen dazu bei, die Systeme effizient und reaktionsschnell zu halten.

Ihre Webhooks werden sofort bei Eintritt eines Ereignisses aufgerufen. Können wir Ihre URL nicht erreichen oder antwortet sie mit einem Fehlerstatus, wiederholen wir den Versand nach einem festen Plan und prüfen über mehrere Tage hinweg automatisch, ob der Empfänger dauerhaft ausfällt. Details dazu finden Sie weiter unten unter Zustellung & Retries.


Zustellung & Retries

Wann wir einen Webhook wiederholen

Ein Webhook wird ausschließlich dann erneut zugestellt, wenn die Antwort Ihres Servers auf einen temporären Fehler hindeutet:

  • Name
    HTTP 429
    Type
    Rate-Limit
    Description

    Ihr Endpunkt signalisiert, dass er gerade zu viele Anfragen erhält.

  • Name
    HTTP 5xx
    Type
    Server-Fehler
    Description

    Konkret 500, 502, 503 und 504 sowie die Cloudflare-Statuscodes 520 bis 524 (z.B. 521 „Web Server Is Down", das Cloudflare bei proxied Domains anstelle von 502 zurückgibt).

  • Name
    Verbindungsfehler
    Type
    Netzwerk
    Description

    Timeout, DNS-Auflösungsfehler, TLS-/Handshake-Fehler oder ein Verbindungsabbruch während des Requests.

Alle übrigen Antworten gelten als final und führen nicht zu einem erneuten Versand. Das gilt auch für 4xx-Codes wie 400, 401, 403 oder 404, da diese in der Regel auf einen Konfigurationsfehler bei der Empfängerseite hinweisen, der durch ein bloßes Wiederholen nicht behoben werden kann. Antworten im Bereich 2xx und 3xx werden als erfolgreich gewertet.

Request-Timeout

Jeder einzelne Zustellversuch hat ein Timeout von 5 Sekunden. Antwortet Ihr Server nicht in dieser Zeit, werten wir den Versuch als Fehler und planen einen Retry nach dem unten beschriebenen Plan ein.

Retry-Plan

Schlägt ein Versuch fehl, wird der nächste nach einem festen, ansteigenden Intervall geplant. Insgesamt unternehmen wir bis zu 14 Versuche, danach wird der Job verworfen. Die gesamte Zustellspanne beträgt damit ca. 3,5 Tage.

VersuchWartezeit seit vorigem Versuch
1 (initial)sofort
21 Minute
35 Minuten
430 Minuten
560 Minuten
62 Stunden
7 bis 14je 10 Stunden

Jeder Versuch wird separat signiert und trägt frische X-Timestamp-, X-Nonce- und X-Signature-Header, siehe unten unter Signatur prüfen.

Automatische Deaktivierung bei dauerhaften Fehlern

Zusätzlich zu den Einzel-Retries beobachten wir laufend die Tages-Fehlerquote pro Webhook. Ein Webhook wird als „problematisch" eingestuft, sobald an einem Tag

  • die heutige Failure-Rate über 80 % liegt und
  • mindestens 10 Fehler an dem Tag aufgetreten sind.

Ab diesem Zeitpunkt wird der Webhook über mehrere Tage hinweg beobachtet. Verbessert sich das Verhalten nicht, eskaliert das System schrittweise:

TagSchwelle (rolling Failure-Rate)Aktion
1über 80 %Warn-E-Mail Stufe 1
3über 85 %Warn-E-Mail Stufe 2
5über 90 %Warn-E-Mail Stufe 3
7über 95 %Webhook wird automatisch deaktiviert, Eintrag im Audit-Log und Deaktivierungs-E-Mail

Die Tageszähler werden ab dem Tag der Problemerkennung gemessen.

Nach einer Auto-Deaktivierung

Ein automatisch deaktivierter Webhook bleibt inaktiv, bis Sie ihn manuell im Dashboard wieder aktivieren. Nach der Reaktivierung startet die Statistik frisch.

Was zählt als „erfolgreich"?

Für die Failure-Rate-Auswertung gilt jede Antwort als „ok", die kein Retry-Auslöser ist. Konkret zählen 2xx, 3xx und auch 4xx-Antworten (mit Ausnahme von 429) als erfolgreich, weil sie aus Sicht des Versandsystems eine eindeutige, finale Rückmeldung darstellen.


Sicherheit

Quell IP prüfen

Alle Webhooks stammen von unserer IPv4 195.201.160.143 bzw. von der IPv6 2a01:4f8:13a:8e7::2. Sollte sich dies ändern, erfahren Sie alle weiteren Details dazu in unserem Changelog.

Signatur prüfen

Alle Webhooks an Ihren Server werden mit dem Signierschlüssel Ihres Accounts signiert. Anhand dieser Daten können Sie bei jedem empfangenen Webhook die Echtheit der Requests von uns validieren und eine Doppelung oder betrügerische Anfragen vermeiden. Überprüfen Sie dazu die Signatur wie auf der Seite Signierung beschrieben.

Folgende Header senden wir Ihnen in jedem Webhook:

  • Name
    X-Signature
    Type
    string
    Description

    Die von uns generierte Signatur

  • Name
    X-Timestamp
    Type
    integer
    Description

    Zeitstempel, zu dem die Signatur von uns erstellt wurde

  • Name
    X-Nonce
    Type
    string
    Description

    Zufällig generierter String mit 32 Zeichen

Jeder einzelne Zustellversuch wird separat signiert. Der erste Versand, jeder automatische Retry aus dem Retry-Plan und ein manueller erneuter Versand aus dem Webhook-Debugger tragen jeweils einen eigenen X-Timestamp, ein eigenes X-Nonce und eine dazu passende X-Signature. Ein Retry, der Stunden nach dem Ereignis eintrifft, trägt also weiterhin einen aktuellen Zeitstempel und ein Nonce, das vorher nie verwendet wurde.

Damit gelten das 30-Sekunden-Fenster für den Zeitstempel und die Einzigartigkeit des Nonce aus der Seite Signierung unverändert auch für Retries. Belassen Sie Ihr Prüffenster bei 30 Sekunden und weiten Sie es für Retries nicht auf.


Beispiele als JSON Payload

SMS

Eingehende SMS (sms_mo)

{
  "data": {
    "id": "681590",
    "sender": "SMS",
    "system": "491771783130",
    "text": "Hello World",
    "time": "1605878104",
    "message_type": "SMS"
  },
  "webhook_event": "sms_mo",
  "webhook_timestamp": "2020-12-02 11:55:44"
}

SMS-Statusänderung (dlr)

{
  "data": {
    "msg_id": "77149843739",
    "status": "TRANSMITTED",
    "timestamp": "1760972114.3369"
  },
  "webhook_event": "dlr",
  "webhook_timestamp": "1760973114.1684"
}

Performance Tracking (tracking)

{
    "webhook_event": "tracking",
    "webhook_timestamp": "2022-07-27T07:38:18+02:00",
    "data": {
        "sms_id": "77182424125",
        "sms_label": null,
        "tracking_url": "https://svn.me/7oz",
        "final_url": "https://www.google.com",
        "type": "click",
        "total_clicks": 5,
        "total_views": 3
    }
}

Voice

Sprachnachricht-Statusänderung (voice_status)

{
  "data": {
    "callerId": "49176123456789",
    "duration": "4",
    "id": "284195",
    "pricePerMinute": 0.075,
    "recipient": "4943160049851",
    "status": "completed",
    "timestamp": 1629786769
  },
  "webhook_event": "voice_status",
  "webhook_timestamp": "2021-08-24T08:32:50+02:00"
}

Eingehender Anruf (voice_call)

{
  "webhook_event": "voice_call",
  "webhook_timestamp": "2024-08-02T07:28:59+02:00",
  "data": {
    "id": 0,
    "caller": "4943160049851",
    "time": 1722576539,
    "system": "4915170517246"
  }
}

DTMF-Signal (voice_dtmf)

{
  "webhook_event": "voice_dtmf",
  "webhook_timestamp": "2024-08-02T07:28:59+02:00",
  "data": {
    "id": 0,
    "callerId": "4943160049851",
    "recipient": "4943160049851",
    "status": "completed",
    "system": "4915170517246",
    "timestamp": 1722576539,
    "duration": 2.76,
    "pricePerMinute": 0.045,
    "dtmf_digit": 9,
    "total_price": 0.045
  }
}

RCS

Nachricht wurde zugestellt

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T10:01:07+01:00",
  "data":
  {
    "msg_id": "77233699836",
    "status": "DELIVERED",
    "timestamp": "1709888466.254410",
    "foreign_id": null,
    "agent_id": "myfancyagent"
  }
}

Nachricht wurde gelesen

{
    "webhook_event": "rcs",
    "webhook_timestamp": "2024-03-08T10:01:09+01:00",
    "data":
    {
      "msg_id": "77233699836",
      "status": "READ",
      "timestamp": "1709888468.065783",
      "foreign_id": null,
      "agent_id": "myfancyagent"
    }
  }

Statusbericht einer gesendeten Nachricht

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T10:00:18+01:00",
  "data":
  {
    "sender": "4915153952979",
    "status": "IS_TYPING",
    "agent_id": "myfancyagent"
  }
}

Es wurde eine Textnachricht gesendet

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T09:47:12+01:00",
  "data": {
    "id": 1871353,
    "sender": "4915153952979",
    "time": 1709870553,
    "message_type": "RCS",
    "content_type": "text",
    "text": "Hallo",
    "agent_id": "myfancyagent"
  }
}

Es wurde eine vorgeschlagene Antwort ausgewählt

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T05:59:33+01:00",
  "data": {
    "id": 1870983,
    "sender": "4915153952979",
    "time": 1709870129,
    "message_type": "RCS",
    "content_type": "suggestion_response",
    "suggestion_response": {
      "postbackData": "suggestion_2",
      "text": "Suggestion #2",
      "type": "REPLY"
    },
    "agent_id": "myfancyagent"
  }
}

Es wurde ein Bild gesendet

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T08:58:20+01:00",
  "data": {
    "id": 1871195,
    "sender": "4915153952979",
    "time": 1709870556,
    "message_type": "RCS",
    "content_type": "user_file",
    "user_file": {
      "thumbnail": {
        "mimeType": "image/jpeg",
        "fileSizeBytes": 10166,
        "fileUri": "https://static.seven.io/uploads/rbm/61513d3d/6176746a5177553d/4f36357145416a773452537737696e495a366a68454a42344639574937716e62704156765358664d486e42776730337334675a76522b6d4f574b6e32626632546842493d.jpeg"
      },
      "payload": {
        "mimeType": "image/jpeg",
        "fileSizeBytes": 611314,
        "fileName": "IMG_20240308_050231_01.jpg",
        "fileUri": "https://static.seven.io/uploads/rbm/61513d3d/6176746a5177553d/4f76746e45515768746866717653444e6166336a513852375249614d753637656f414a6f4758624d5358417269306e767331493645372b47446654325a66764b3168453d.jpeg"
      }
    },
    "agent_id": "myfancyagent"
  }
}

Es wurde eine Datei gesendet

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T11:07:51+01:00",
  "data": {
    "id": 1871485,
    "sender": "4915153952979",
    "time": 1709892233,
    "message_type": "RCS",
    "content_type": "user_file",
    "user_file": {
      "payload": {
        "mimeType": "application/pdf",
        "fileSizeBytes": 18810,
        "fileName": "sample (1).pdf",
        "fileUri": "https://static.seven.io/uploads/rbm/61513d3d/617678715267303d/4f616c675256696b73524c6c75337a4b5a66336a51705570464e665a752f6d4e70464d37546e504b5458423730556a7337314e70514c3241436650304d616e4b3078493d.pdf"
      }
    },
    "agent_id": "myfancyagent"
  }
}

Es wurde ein Standort geteilt

{
  "webhook_event": "rcs",
  "webhook_timestamp": "2024-03-08T11:07:51+01:00",
  "data": {
    "id": 1871485,
    "sender": "4915153952979",
    "time": 1709892233,
    "message_type": "RCS",
    "content_type": "location",
    "location": {
        "latitude": 54.3216562,
        "longitude": 10.1350767
    },
    "agent_id": "myfancyagent"
  }
}

WhatsApp

Eingehende WhatsApp-Nachrichten werden mit dem Event wa_mo zugestellt. Das data-Objekt enthält immer dieselben Basisfelder, unabhängig davon, was der Kunde gesendet hat:

  • Name
    id
    Type
    integer
    Description

    Interne ID der eingehenden Nachricht

  • Name
    sender
    Type
    string
    Description

    Rufnummer des Absenders im internationalen Format ohne führendes Plus

  • Name
    time
    Type
    integer
    Description

    Unix-Zeitstempel des Empfangs

  • Name
    text
    Type
    string
    Description

    Nachrichteninhalt. Klartext bei Textnachrichten, ein JSON-kodierter String bei allen anderen Inhaltstypen

  • Name
    message_type
    Type
    string
    Description

    Immer WA

  • Name
    system
    Type
    string
    Description

    Ihre WhatsApp-Business-Nummer, an die die Nachricht gesendet wurde

Setzen Sie am Webhook einen Event-Filter, wird dieser gegen system geprüft. So können Sie Nachrichten je WhatsApp-Business-Nummer verteilen.

Eingehende Textnachricht (wa_mo)

{
  "webhook_event": "wa_mo",
  "webhook_timestamp": "2026-08-21T14:50:12+02:00",
  "data": {
    "id": 4521987,
    "sender": "353861234567",
    "time": 1755780612,
    "text": "Hello World",
    "message_type": "WA",
    "system": "4915170517246"
  }
}

Andere Inhaltstypen als reiner Text

Medien, Standorte, Kontakte, Quick-Reply-Buttons und interaktive Antworten werden im selben Umschlag zugestellt. Das Feld text enthält dann einen JSON-kodierten String, dessen Schlüssel type angibt, was empfangen wurde:

typeVom Kunden gesendet
user_fileBild, Video, Audio, Dokument oder Sticker
locationEin geteilter Standort
contactEine geteilte Kontaktkarte
buttonQuick-Reply-Button einer Template-Nachricht
interactiveAntwort auf eine interaktive Nachricht (Button oder Liste)

Dekodierter Inhalt des Feldes text:

Medien (user_file)

{
  "type": "user_file",
  "user_file": [
    {
      "type": "image",
      "mediaId": "1234567890123456",
      "mimeType": "image/jpeg",
      "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
      "fileName": "IMG_20260821_145012.jpg",
      "fileUri": "https://static.seven.io/uploads/whatsapp/61513d3d/6176746a/4f36357145416a773452537737/IMG_20260821_145012.jpg",
      "caption": "Schau dir das an"
    }
  ]
}

type wird aus dem MIME-Typ abgeleitet und ist einer von image, video, audio, document oder sticker. Können wir die Datei nicht von Meta herunterladen, fehlt fileUri und das Objekt enthält stattdessen "downloadFailed": true. Nutzen Sie in diesem Fall mediaId, um die Datei selbst abzurufen.

Standort

{
  "type": "location",
  "location": {
    "latitude": 54.3216562,
    "longitude": 10.1350767,
    "name": "seven communications GmbH",
    "address": "Alte Weide 7, 24116 Kiel"
  }
}

Kontakt

{
  "type": "contact",
  "contact": {
    "formattedName": "John Doe",
    "firstName": "John",
    "lastName": "Doe",
    "middleName": null,
    "phones": [{"phone": "+353861234567", "type": "CELL"}],
    "emails": [{"email": "john@example.com", "type": "WORK"}],
    "addresses": [],
    "company": "Example Ltd",
    "birthday": "1985-04-12"
  }
}

Quick-Reply-Button

{
  "type": "button",
  "button": {
    "text": "Termin bestätigen",
    "payload": "confirm_2026_08_21"
  }
}

Interaktive Antwort

{
  "type": "interactive",
  "interactive": {
    "item_id": "option_2",
    "title": "Option #2",
    "description": "Lieferung am Freitag"
  }
}
Zuletzt aktualisiert: Vor 7 Stunden