Webhooks
CRM-Events abonnieren, Signaturen verifizieren, fehlgeschlagene Deliveries prüfen und den vollständigen Event-Katalog auf einer Seite lesen.
Customermates sendet einen JSON-POST an Ihren HTTPS-Endpunkt, sobald sich abonnierte Daten ändern. Sie wählen die Events und geben die URL an. Fehlgeschlagene Deliveries können Sie aus der UI oder per MCP erneut zustellen. Deliveries werden per HMAC-SHA256 über den rohen Request-Body signiert. Jedes Event und seine Payload-Struktur stehen im Katalog am Ende dieser Seite.
Einsatzzwecke
Webhooks lassen andere Systeme auf CRM-Änderungen reagieren, ohne zu pollen.
- Neue Contacts in einen E-Mail-Sequencer übergeben.
- Ein Ticket im Support-Tool öffnen, wenn ein Deal seinen Zustand ändert.
- Einen Finance-Workflow auslösen, wenn ein Deal als abgeschlossen markiert wird.
- Records nahezu in Echtzeit in ein Data Warehouse synchronisieren.
Webhook anlegen
UI: Unternehmen, dann Webhooks, dann Neu.
MCP: manage_webhooks mit action: "create", dazu url, events[] und optional description, secret, enabled.
{
"url": "https://hooks.example.com/customermates",
"events": ["contact.created", "contact.updated", "deal.updated"],
"secret": "nutze-einen-random-string-aus-einem-password-manager",
"enabled": true
}Die URL muss HTTPS sein. HTTP wird abgelehnt.
Was Sie bekommen
Jede Delivery ist ein POST mit Content-Type: application/json und demselben Envelope:
{
"event": "<event-name>",
"data": {
"userId": "<verursacher>",
"companyId": "<workspace>",
"entityId": "<betroffener-record>",
"payload": { /* event-spezifisch */ }
},
"timestamp": "<iso-8601>"
}userId ist der User, der den Write verursacht hat, inklusive User, die über einen API-Key handeln. Bei Messaging-Events, die durch eingehende Provider-Aktivität statt durch eine User-Aktion ausgelöst werden, ist userId gleich null.
Bei *.updated-Events umschließt payload den vollständigen Record zusammen mit einem changes-Objekt. Bei *.created- und *.deleted-Events ist payload der Record selbst.
Beispiel, contact.updated:
{
"event": "contact.updated",
"data": {
"userId": "u_123",
"companyId": "c_abc",
"entityId": "ct_xyz",
"payload": {
"contact": {
"id": "ct_xyz",
"firstName": "Max",
"lastName": "Mustermann",
"notes": { /* Tiptap-JSON */ },
"organizations": [{ "id": "org_1", "name": "Example GmbH" }],
"users": [],
"deals": [{ "id": "deal_1" }, { "id": "deal_2" }],
"customFieldValues": [
{ "columnId": "col_abc", "value": "Won" }
]
},
"changes": {
"organizations": {
"previous": [],
"current": [{ "id": "org_1", "name": "Example GmbH" }]
}
}
}
},
"timestamp": "2026-04-22T10:00:00.000Z"
}Die Record-Felder hängen vom Entity-Typ ab. Custom-Columns erscheinen unter customFieldValues als columnId- und value-Paare. Mit get_record_schema sehen Sie die Columns, die für einen Entity-Typ in Ihrem Workspace konfiguriert sind.
Das changes-Objekt
changes existiert nur bei *.updated-Events. Jeder Key ist ein Record-Feld, dessen Wert sich geändert hat. previous ist der Wert vor dem Write, current der Wert danach.
Arrays verknüpfter Records werden per id verglichen. Objekte per Deep-Equal. Skalare Felder per Wert. createdAt und updatedAt werden nie als Änderung gemeldet.
Wenn ein Write kein Feld ändert, wird das Event unterdrückt. No-op-*.updated-Events werden nicht zugestellt.
Signatur-Verifikation
Wenn Sie ein secret setzen, enthält jede Request:
X-Webhook-Signature: <hex><hex> ist HMAC-SHA256(secret, rawRequestBody), Lowercase-Hex, ohne Präfix. Berechnen Sie den Wert auf Ihrer Seite neu und vergleichen Sie in konstanter Zeit.
Node.js-Beispiel:
import crypto from "crypto";
function verify(rawBody: string, received: string, secret: string) {
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
}Retries und fehlgeschlagene Deliveries
Jede Delivery wird protokolliert. Deliveries sehen Sie in der UI (Unternehmen, dann Webhook-Deliveries) oder per manage_webhooks mit action: "list_deliveries".
Eine Delivery gilt als fehlgeschlagen, wenn der HTTP-Status außerhalb des 2xx-Bereichs liegt oder die Request timeoutet. Der Request-Timeout beträgt 5 Sekunden.
Customermates wiederholt fehlgeschlagene Deliveries automatisch: bis zu 5 Wiederholungen bei Timeouts, 5xx-Responses und 408, 425 und 429. Dauerhafte 4xx-Responses (zum Beispiel 400, 401, 403, 404) werden nicht wiederholt, weil ein erneuter Versuch genauso scheitern würde. Die Retries laufen innerhalb des Delivery-Runs, erzeugen also keine zusätzlichen Delivery-Records; jeder Record zeigt das Endergebnis.
Sie können jede Delivery zusätzlich selbst erneut zustellen, aus der UI oder per manage_webhooks mit action: "resend_delivery" und der Delivery-id. Ein manueller Resend erzeugt einen neuen Delivery-Record; das Original bleibt unverändert.
Ordering und Concurrency
Deliveries sind über Events hinweg nicht strikt geordnet. Zwei contact.updated-Events im Millisekundenabstand können in falscher Reihenfolge ankommen. Löse die Reihenfolge über den timestamp im Payload auf oder behandle Deliveries als idempotente Messages mit entityId als Schlüssel.
Notes sind JSON, nicht Markdown
Das notes-Feld in Payloads ist Tiptap-JSON, dieselbe Struktur, die der Editor speichert. Um es auf Ihrer Seite als Markdown zu rendern, nutzen Sie einen Tiptap-kompatiblen Serializer. In MCP liefert get_records mit include: "withNotes" die Notes bereits als Markdown serialisiert.
Delete-Payloads
Delete-Events enthalten unter payload den vollständigen Record, wie er vor dem Löschen war, nicht nur die id. Die id des betroffenen Records steht zusätzlich in data.entityId.
Debugging
- Nutzen Sie webhook.site als Einweg-Receiver beim Integrationstest.
- Das Delivery-Log (
manage_webhooks,action: "list_deliveries") unterstütztsearchTermauf URL und Event-Name. - Wenn Ihr Receiver einen Fehlerstatus zurückgibt, protokolliert Customermates die Antwort, sodass Sie nachvollziehen können, was passiert ist.
Event-Katalog
Sechsundzwanzig Events können Sie abonnieren: fünfzehn Record-Events über die fünf Entity-Typen und elf Messaging-Events. Alle nutzen den Envelope oben.
Record-Events
| Event | Wann es feuert | payload |
|---|---|---|
contact.created | Ein Contact wird via UI, API, MCP oder Import erstellt | vollständiger Contact |
contact.updated | Ein Contact-Feld ändert sich | contact, changes |
contact.deleted | Ein Contact wird gelöscht | vollständiger Contact |
organization.created | Eine Organization wird erstellt | vollständige Organization |
organization.updated | Ein Organization-Feld ändert sich | organization, changes |
organization.deleted | Eine Organization wird gelöscht | vollständige Organization |
deal.created | Ein Deal wird erstellt | vollständiger Deal |
deal.updated | Ein Deal-Feld ändert sich | deal, changes |
deal.deleted | Ein Deal wird gelöscht | vollständiger Deal |
service.created | Ein Service wird erstellt | vollständiger Service |
service.updated | Ein Service-Feld ändert sich | service, changes |
service.deleted | Ein Service wird gelöscht | vollständiger Service |
task.created | Ein Task wird erstellt | vollständiger Task |
task.updated | Ein Task-Feld ändert sich | task, changes |
task.deleted | Ein Task wird gelöscht | vollständiger Task |
Messaging-Events
Messaging-Events feuern bei eingehender Provider-Aktivität auf verbundenen Accounts. userId ist null. Payloads referenzieren Provider-Records per id (connectedAccountId, provider, providerMessageId, threadId und ähnliche), statt CRM-Records einzubetten.
| Event | Wann es feuert |
|---|---|
messaging.message.received | Eine Chat-Nachricht oder E-Mail wird ingested |
messaging.message.updated | Eine ingestete Nachricht wird aktualisiert |
messaging.message.deleted | Eine ingestete Nachricht wird gelöscht |
messaging.message.reaction | Eine Reaktion wird zu einer Nachricht hinzugefügt |
messaging.email.received | Eine E-Mail wird ingested |
messaging.email.deleted | Eine ingestete E-Mail wird gelöscht |
messaging.chat.updated | Ein Chat-Thread wird aktualisiert |
messaging.chat.deleted | Ein Chat-Thread wird gelöscht |
messaging.calendar.changed | Ein verbundener Kalender ändert sich |
messaging.calendar_event.changed | Ein Kalender-Event ändert sich |
messaging.relation.created | Eine neue Provider-Relation wird erstellt |
Weiter
- MCP-Tool-Katalog: Webhooks programmatisch verwalten.
- n8n-Integration: Webhooks in visuelle Workflows einbinden.