Webhooks sind HTTP-Callbacks, die für jedes Konto eingerichtet werden. Sie werden ausgelöst, wenn Aktionen wie das Erstellen einer Nachricht in Chatwoot stattfinden. Für ein einzelnes Konto können mehrere Webhooks erstellt werden.

## Wie füge ich einen Webhook hinzu?

**Schritt 1.** Gehe zu **Einstellungen → Integrationen → Webhooks**. Klicke auf die Schaltfläche "Konfigurieren".

![](https://app.chatwoot.com/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBNzU1VHc9PSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--d54d34aa968f1291a3d3882a29abbd8056eb6a09/how-to-find-webhooks-setting-in-chatwoot.png)

**Schritt 2.** Klicke auf die Schaltfläche "Neuen Webhook hinzufügen". Ein Modal öffnet sich. Gib hier die URL ein, an die die POST-Anfrage gesendet werden soll. Anschließend musst du die Ereignisse auswählen, die du abonnieren möchtest. Mit dieser Option kannst du nur relevante Ereignisse in Chatwoot überwachen.

![](https://app.chatwoot.com/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBOEo1VHc9PSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--99a0d32f0c3f8e39f81b49d9fa834ac97efa4f0f/webhook%20setting.png)

Chatwoot sendet für verschiedene Aktualisierungen in deinem Konto eine POST-Anfrage mit folgendem Payload an die konfigurierten URLs.

### Ein Beispiel für ein Webhook-Payload

```
{

  "event": "message_created", // Der Name des Ereignisses
  "id": "1", // Nachrichten-ID
  "content": "Hi", // Inhalt der Nachricht
  "created_at": "2020-03-03 13:05:57 UTC", // Zeitpunkt, zu dem die Nachricht gesendet wurde
  "message_type": "incoming", // Dieser Wert kann incoming, outgoing oder template sein. Der Nutzer vom Widget sendet eingehende Nachrichten, der Agent sendet ausgehende Nachrichten an den Nutzer.
  "content_type": "enum", // Dies ist ein Enum, es kann input_select, cards, form oder text sein. Der message_type ist template, falls content_type einer davon ist. Standardwert ist text.
  "content_attributes": {} // Dies ist ein Objekt, verschiedene Werte sind unten definiert
  "source_id": "", // Dies ist die externe ID, falls der Posteingang eine Twitter- oder Facebook-Integration ist.
  "sender": { // Enthält die Details des Agenten, der diese Nachricht gesendet hat
    "id": "1",
    "name": "Agent",
    "email": "agent@example.com"
  },
  "contact": { // Enthält die Details des Nutzers, der diese Nachricht gesendet hat
    "id": "1",
    "name": "contact-name"
  },
  "conversation": { // Enthält die Details der Konversation
    "display_id": "1", // Die ID der Konversation, wie sie im Dashboard zu sehen ist.
    "additional_attributes": {
      "browser": {
        "device_name": "Macbook",
        "browser_name": "Chrome",
        "platform_name": "Macintosh",
        "browser_version": "80.0.3987.122",
        "platform_version": "10.15.2"
      },
      "referer": "<http://www.chatwoot.com>",
      "initiated_at": "Tue Mar 03 2020 18:37:38 GMT-0700 (Mountain Standard Time)"
    }
  },
  "account": { // Enthält die Details des Kontos
    "id": "1",
    "name": "Chatwoot",
  }
}
```

## Unterstützte Webhook-Ereignisse in Chatwoot

Chatwoot veröffentlicht verschiedene Ereignisse an die konfigurierten Webhook-Endpunkte. Wenn du einen Webhook konfigurieren möchtest, findest du eine Anleitung [hier](https://www.chatwoot.com/hc/user-guide/articles/1784681468-wie-verwendet-man-webhooks).

Jedes Ereignis hat eine eigene Payload-Struktur, die sich nach dem jeweiligen Modelltyp richtet. Im folgenden Abschnitt werden die Hauptobjekte beschrieben, die wir in Chatwoot verwenden, sowie deren Attribute.

## Objekte

Ein Event-Payload kann eines der folgenden Objekte enthalten. Die verschiedenen von Chatwoot unterstützten Objekttypen sind unten aufgelistet.

**Konto**

```
{
  "id": "integer",
  "name": "string"
}
```

**Posteingang**

```
{
"id": "integer",
"name": "string"
}
```

**Kontakt**

```
{
  "id": "integer",
  "name": "string",
  "avatar": "string",
  "type": "contact",
  "account": {
    // <...Account Object>
  }
}
```

**Benutzer**

```
{
  "id": "integer",
  "name": "string",
  "email": "string",
  "type": "user"
}
```

**Konversation**

```
{
  "additional_attributes": {
    "browser": {
      "device_name": "string",
      "browser_name": "string",
      "platform_name": "string",
      "browser_version": "string",
      "platform_version": "string"
    },
    "referer": "string",
    "initiated_at": {
      "timestamp": "iso-datetime"
    }
  },
  "can_reply": "boolean",
  "channel": "string",
  "id": "integer",
  "inbox_id": "integer",
  "contact_inbox": {
    "id": "integer",
    "contact_id": "integer",
    "inbox_id": "integer",
    "source_id": "string",
    "created_at": "datetime",
    "updated_at": "datetime",
    "hmac_verified": "boolean"
  },
  "messages": ["Array of message objects"],
  "meta": {
    "sender": {
      // Contact Object
    },
    "assignee": {
      // User Object
    }
  },
  "status": "string",
  "unread_count": "integer",
  "agent_last_seen_at": "unix-timestamp",
  "contact_last_seen_at": "unix-timestamp",
  "timestamp": "unix-timestamp",
  "account_id": "integer"
}
```

**Nachricht**

```
{
  "id": "integer",
  "content": "string",
  "message_type": "integer",
  "created_at": "unix-timestamp",
  "private": "boolean",
  "source_id": "string / null",
  "content_type": "string",
  "content_attributes": "object",
  "sender": {
    "type": "string - contact/user"
    // User oder Contact Object
  },
  "account": {
    // Account Object
  },
  "conversation": {
    // Conversation Object
  },
  "inbox": {
    // Inbox Object
  }
}
```

**Ein Beispiel für ein Webhook-Payload**

```
{
  "event": "event_name"
  // Attribute, die sich auf das Ereignis beziehen
}
```

## Webhook-Ereignisse

Chatwoot unterstützt die folgenden Webhook-Ereignisse. Du kannst sie beim Konfigurieren eines Webhooks im Dashboard oder über die API abonnieren.

### conversation_created

Dieses Ereignis wird ausgelöst, wenn im Konto eine neue Konversation erstellt wird. Das Payload für dieses Ereignis sieht wie folgt aus.

```
{
  "event": "conversation_created"
  // <...Konversations-Attribute>
}
```

### conversation_updated

Dieses Ereignis wird ausgelöst, wenn sich eines der Attribute der Konversation ändert.

```
{
  "event": "conversation_updated",
  "changed_attributes": [
    {
      "<attribute_name>": {
        "current_value": "",
        "previous_value": ""
      }
    }
  ]
  // <...Konversations-Attribute>
}
```

### conversation_status_changed

Dieses Ereignis wird ausgelöst, wenn sich der Status der Konversation ändert.

Hinweis: Wenn du Agent Bot APIs statt Webhooks verwendest, wird dieses Ereignis derzeit noch nicht unterstützt.

```
{
  "event": "conversation_status_changed"
  // <...Konversations-Attribute>
}
```

### message_created

Dieses Ereignis wird ausgelöst, wenn eine Nachricht in einer Konversation erstellt wird. Das Payload für dieses Ereignis sieht wie folgt aus.

```
{
  "event": "message_created"
  // <...Nachrichten-Attribute>
}
```

### message_updated

Dieses Ereignis wird ausgelöst, wenn eine Nachricht in einer Konversation aktualisiert wird. Das Payload für dieses Ereignis sieht wie folgt aus.

```
{
  "event": "message_updated"
  // <...Nachrichten-Attribute>
}
```

### webwidget_triggered

Dieses Ereignis wird ausgelöst, wenn der Endnutzer das Live-Chat-Widget öffnet.

```
{
  "event": "webwidget_triggered",
  "id": "",
  "contact": {
    // <...Kontakt-Objekt>
  },
  "inbox": {
    // <...Posteingang-Objekt>
  },
  "account": {
    // <...Konto-Objekt>
  },
  "current_conversation": {
    // <...Konversations-Objekt>
  },
  "source_id": "string",
  "event_info": {
    "initiated_at": {
      "timestamp": "date-string"
    },
    "referer": "string",
    "widget_language": "string",
    "browser_language": "string",
    "browser": {
      "browser_name": "string",
      "browser_version": "string",
      "device_name": "string",
      "platform_name": "string",
      "platform_version": "string"
    }
  }
}
```

### conversation_typing_on

Dieses Ereignis wird ausgelöst, wenn ein Agent beginnt, in einer Konversation zu tippen. Es kann sich um eine private Notiz oder um eine Nachricht an den Kunden handeln. Mit dem `is_private`-Flag kannst du zwischen beiden unterscheiden.

```
{
  "event": "conversation_typing_on",
  "conversation": { ...<Konversations-Objekt> },
  "user": { ... <User / AgentBot / Captain Objekt> },
  "is_private": true
}
```

### conversation_typing_off

Dieses Ereignis wird ausgelöst, wenn ein Agent aufhört zu tippen oder das Konversationsfenster verlässt.

```
{
  "event": "conversation_typing_off",
  "conversation": { ...<Konversations-Objekt> },
  "user": { ... <User / AgentBot / Captain Objekt> },
  "is_private": true
}
```

# Überprüfung von Webhooks

Chatwoot signiert jede ausgehende Webhook-Anfrage, damit dein Server verifizieren kann, dass das Payload tatsächlich von Chatwoot gesendet und nicht manipuliert wurde. Das Secret wird dir angezeigt, sobald der Webhook erstellt ist. Du kannst es im Webhook-Bearbeitungsformular erneut einsehen.

Jede Webhook-Anfrage sendet die folgenden Header, die zur Berechnung der HMAC-Signatur des Payloads verwendet werden können

* `X-Chatwoot-Signature`: HMAC-SHA256-Signatur mit Präfix `sha256=`

* `X-Chatwoot-Timestamp`: Unix-Timestamp (Sekunden), zu dem die Anfrage signiert wurde

* `X-Chatwoot-Delivery`: Eindeutige Liefer-ID für das Webhook-Ereignis (falls verfügbar)

Die Signatur wird wie folgt berechnet:

```
sha256=HMAC-SHA256(webhook_secret, "{timestamp}.{raw_body}")
```

Dabei gilt:

* `webhook_secret` ist das mit dem Webhook verknüpfte Secret

* `timestamp` ist der Wert des Headers `X-Chatwoot-Timestamp`

* `raw_body` ist der rohe JSON-Request-Body (nicht geparst/neu serialisiert)

## Überprüfungsschritte

1. `X-Chatwoot-Signature` und `X-Chatwoot-Timestamp` aus den Request-Headern extrahieren

2. Den rohen Request-Body als Bytes lesen (nicht parsen oder neu serialisieren)

3. Die erwartete Signatur berechnen: `sha256=HMAC-SHA256(secret, "{timestamp}.{raw_body}")`

4. Die berechnete Signatur und die empfangene Signatur mit einem konstantzeitlichen Vergleich vergleichen

5. Optional: Anfragen ablehnen, bei denen der Timestamp zu alt ist, um Replay-Angriffe zu verhindern

## Beispiele

### Ruby

```
def verify_signature(raw_body, timestamp, received_signature, secret)
  expected = "sha256=#{OpenSSL::HMAC.hexdigest('SHA256', secret, "#{timestamp}.#{raw_body}")}"
  ActiveSupport::SecurityUtils.secure_compare(expected, received_signature)
end
```

### Python

```
import hmac
import hashlib

def verify_signature(raw_body: bytes, timestamp: str, received_signature: str, secret: str) -> bool:
    message = f"{timestamp}.".encode() + raw_body
    expected = "sha256=" + hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received_signature)
```

### Node.js

```

const crypto = require("crypto");

function verifySignature(rawBody, timestamp, receivedSignature, secret) {
  const message = `${timestamp}.${rawBody}`;
  const expected =
    "sha256=" + crypto.createHmac("sha256", secret).update(message).digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(receivedSignature)
  );
}
```

### Go

```
import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
)

func verifySignature(rawBody []byte, timestamp, receivedSignature, secret string) bool {
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(fmt.Sprintf("%s.%s", timestamp, rawBody)))
	expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
	return hmac.Equal([]byte(expected), []byte(receivedSignature))
}
```

## Wichtige Hinweise

* Verwende immer **den rohen Request-Body** für die Verifizierung. Das Parsen und erneute Serialisieren des JSON kann die Reihenfolge der Schlüssel, Whitespace oder Unicode-Escaping ändern, was zu einer anderen Signatur führt.

* Verwende immer einen **konstantzeitlichen Vergleich** (z.B. `hmac.compare_digest`, `crypto.timingSafeEqual`, `ActiveSupport::SecurityUtils.secure_compare`), um Timing-Angriffe zu verhindern.

* Erwäge, Anfragen mit Timestamps, die älter als 5 Minuten sind, abzulehnen, um Replay-Angriffe zu verhindern.