Webhooks
Endpunkte registrieren, die signierte Event-Benachrichtigungen empfangen.
Webhook erstellen
Erstellt einen Webhook-Endpunkt in Ihrem Workspace. Emailit sendet passende Events in Batches von bis zu 100 an die URL, als JSON-Array, das mit dem secret des Webhooks signiert ist. Das Anfrageformat finden Sie unter Webhook-Anfragen. Erfordert einen API-Schlüssel mit dem Scope full.
/webhooksAnfrage-Body
namestringErforderlichName des Webhooks. Muss im Workspace eindeutig sein; Sie können ihn in den anderen API-Endpunkten für Webhooks statt der ID verwenden.
urlstringErforderlichEndpunkt, der die Events empfängt. http- und https-URLs werden akzeptiert; verwenden Sie im Produktivbetrieb https.
Emailit löst den Hostnamen beim Speichern auf und lehnt localhost, private, Link-Local- und andere reservierte IP-Adressen ab. Bei der Zustellung folgt Emailit keinen HTTP-Redirects; verwenden Sie also die endgültige URL.
all_eventsbooleanAlle Event-Typen senden, auch später hinzukommende. Standardwert: false. Bei true wird events ignoriert.
enabledbooleanOb Emailit Events an den Webhook zustellt. Standardwert: true.
eventsstring[]Zu sendende Event-Typen, zum Beispiel ["email.delivered", "email.bounced"]. Siehe Event-Typen. Standardwert: []; zusammen mit all_events: false empfängt der Webhook dann nichts.
Event-Namen werden nicht validiert. Ein falsch geschriebener Typ wird gespeichert, passt aber nie zu einem Event.
filterobject | nullPayload-Filter. Emailit sendet nur Events, deren Objekt den Regeln entspricht. Verfügbar in den Tarifen Pro, Business und Custom; ein Filter mit Regeln gibt bei Pay as you go 403 zurück.
filter.matchstringall (Standardwert) sendet ein Event, wenn alle Regeln zutreffen. any sendet es, wenn mindestens eine Regel zutrifft.
filter.rulesobject[]Bis zu 25 Regeln.
filter.rules[].fieldstringErforderlichPfad in Punktnotation innerhalb des Event-Objekts, zum Beispiel to, status, meta.plan oder bei Klick- und Öffnungs-Events email.campaign.id. Ein vorangestelltes payload. wird ignoriert.
filter.rules[].operatorstringErforderlichequals, not_equals, contains, not_contains, starts_with, ends_with, greater_than, less_than, is_set, is_not_set, in oder not_in. Textoperatoren vergleichen Werte als Strings; greater_than und less_than vergleichen Zahlen.
filter.rules[].valueanyVergleichswert. Erforderlich für alle Operatoren außer is_set und is_not_set. Verwenden Sie bei in und not_in ein Array.
Rückgabe
Gibt 201 Created mit dem Webhook-Objekt zurück, einschließlich des Signatur-Secrets secret (whsec_ gefolgt von 64 Hexadezimalzeichen). Mit dem Secret verifizieren Sie die Signaturen der Anfragen. Sie können es mit Webhook abrufen erneut auslesen und mit Signatur-Secret rotieren rotieren.
| Status | Wann |
|---|---|
400 |
name oder url fehlt, die URL ist ungültig, lässt sich nicht auflösen oder zeigt auf eine blockierte Adresse, oder der Filter ist ungültig. |
403 |
Der Filter hat Regeln und Ihr Tarif enthält keine Webhook-Filter. Der Body lautet {"error": "plan_required", "required_plan": "pro"}. |
409 |
Ein Webhook mit diesem Namen existiert bereits. Der Body enthält in existing die id und den name des vorhandenen Webhooks. |
422 |
Der Workspace hat das Webhook-Limit seines Tarifs erreicht. Der Body enthält usage.used und usage.limit. Siehe Limits. |
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"url": "https://api.acme.com/webhooks/emailit",
"all_events": false,
"enabled": true,
"events": ["email.delivered", "email.bounced"],
"filter": null,
"filters_allowed": true,
"last_used_at": null,
"created_at": "2026-10-01T09:41:05.302000+00:00",
"updated_at": "2026-10-01T09:41:05.302000+00:00",
"secret": "whsec_175a02aca3fb7dc3107ca21e4224a73d058cacc628356a39e020422aec3ca434"
}{
"error": "URL resolves to a private/reserved IP address"
}{
"error": "plan_required",
"required_plan": "pro"
}{
"error": "Webhook with this name already exists",
"existing": {
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events"
}
}{
"error": "Pay as you go includes 3 webhook endpoints.",
"usage": {
"used": 3,
"limit": 3
}
}{
"name": "Enterprise bounces",
"url": "https://api.acme.com/webhooks/emailit",
"events": ["email.bounced", "email.complained"],
"filter": {
"match": "all",
"rules": [
{ "field": "meta.plan", "operator": "equals", "value": "enterprise" },
{ "field": "to", "operator": "not_contains", "value": "@acme.com" }
]
}
}Webhook abrufen
Gibt einen Webhook zurück, gesucht per ID oder Name. Das ist der einzige lesende Endpunkt, der das Signatur-Secret secret zurückgibt. Erfordert einen API-Schlüssel mit dem Scope full.
/webhooks/:idPfadparameter
idstringErforderlichID des Webhooks (wh_…) oder sein Name, URL-kodiert.
Rückgabe
Gibt 200 OK mit dem Webhook-Objekt zurück, einschließlich secret und filters_allowed (ob Ihr Tarif dem Webhook einen Payload-Filter erlaubt). last_used_at ist der Zeitpunkt der letzten erfolgreichen Zustellung oder null, wenn noch nichts zugestellt wurde.
Gibt 404 mit error: "Webhook not found" zurück, wenn kein Webhook passt.
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"url": "https://api.acme.com/webhooks/emailit",
"all_events": false,
"enabled": true,
"events": ["email.delivered", "email.bounced"],
"filter": {
"match": "all",
"rules": [
{ "field": "meta.plan", "operator": "equals", "value": "enterprise" }
]
},
"filters_allowed": true,
"last_used_at": "2026-10-01T10:02:17.845000+00:00",
"created_at": "2026-10-01T09:41:05.302000+00:00",
"updated_at": "2026-10-01T09:41:05.302000+00:00",
"secret": "whsec_175a02aca3fb7dc3107ca21e4224a73d058cacc628356a39e020422aec3ca434"
}{
"error": "Webhook not found"
}Webhook aktualisieren
Aktualisiert einen Webhook. Senden Sie nur die Felder, die Sie ändern möchten; mindestens eines ist erforderlich. Das Signatur-Secret ändert sich nicht; Sie rotieren es mit Signatur-Secret rotieren. Erfordert einen API-Schlüssel mit dem Scope full.
/webhooks/:idPfadparameter
idstringErforderlichID des Webhooks (wh_…) oder sein Name, URL-kodiert.
Anfrage-Body
namestringNeuer Name. Muss im Workspace eindeutig sein.
urlstringNeue URL des Endpunkts, http oder https. Wird genauso validiert wie beim Erstellen.
all_eventsbooleantrue sendet alle Event-Typen und leert die Liste events. Wenn Sie den Wert auf false setzen, senden Sie auch events, sonst empfängt der Webhook nichts.
enabledbooleanfalse stoppt Zustellungen, true nimmt sie wieder auf. Events, die auftreten, während der Webhook deaktiviert ist, werden für ihn nicht in die Warteschlange gestellt und auch später nicht gesendet.
eventsstring[]Ersetzt die Liste der Event-Typen. Wird ignoriert, solange all_events den Wert true hat. Namen werden nicht validiert.
filterobject | nullErsetzt den Payload-Filter, im selben Format wie beim Erstellen. Senden Sie null, um ihn zu entfernen. Ein Filter mit Regeln setzt den Tarif Pro, Business oder Custom voraus.
Rückgabe
Gibt 200 OK mit dem aktualisierten Webhook zurück. Das secret ist nicht enthalten; lesen Sie es mit Webhook abrufen aus.
| Status | Wann |
|---|---|
400 |
Der Body enthält keines der obigen Felder oder URL bzw. Filter sind ungültig. |
403 |
Der Filter hat Regeln und Ihr Tarif enthält keine Webhook-Filter (plan_required). |
404 |
Kein Webhook passt zu id. |
409 |
Ein anderer Webhook verwendet bereits den neuen Namen. |
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"url": "https://api.acme.com/webhooks/emailit",
"all_events": false,
"enabled": false,
"events": ["email.delivered", "email.bounced"],
"filter": null,
"filters_allowed": true,
"last_used_at": "2026-10-01T10:02:17.845000+00:00",
"created_at": "2026-10-01T09:41:05.302000+00:00",
"updated_at": "2026-10-01T10:15:40.000000+00:00"
}{
"error": "No valid fields provided for update. Provide at least one of: name, url, all_events, enabled, events, filter"
}{
"error": "Webhook not found"
}{
"error": "Another webhook with this name already exists"
}Webhooks auflisten
Gibt die Webhooks in Ihrem Workspace zurück, die neuesten zuerst, und wie viele Ihr Tarif erlaubt. Signatur-Secrets sind in der Liste nicht enthalten. Erfordert einen API-Schlüssel mit dem Scope full.
/webhooksQuery-Parameter
pageintegerSeitennummer, beginnend bei 1. Standardwert: 1.
limitintegerWebhooks pro Seite, von 1 bis 100. Standardwert: 10.
searchstringAbgleich mit Name oder URL des Webhooks, ohne Beachtung der Groß-/Kleinschreibung.
matchstringall (Standardwert) verlangt, dass alle Filter zutreffen. or trifft zu, wenn ein beliebiger Filter passt. Siehe Filtern und Sortieren.
orderstringSortierschlüssel für diese Liste. Siehe die Sortierschlüssel unten.
directionstringasc oder desc.
Filter und Sortierung
Listenfilter sind Query-Parameter der Form key.condition=value auf einer einzigen Ebene. match, order, direction und die Bedingungen pro Typ finden Sie unter Filtern und Sortieren.
Filterschlüssel
| Schlüssel | Typ | Bedingungen | Hinweise |
|---|---|---|---|
name | string | exact, not_exact, contains, not_contains, starts_with, ends_with, empty, not_empty | |
url | string | exact, not_exact, contains, not_contains, starts_with, ends_with, empty, not_empty | |
enabled | boolean | exact, not_exact | |
created_at | date | exact, before, after, empty, not_empty |
Sortierschlüssel
Übergeben Sie in order einen dieser Schlüssel und in direction den Wert asc oder desc: name, url, enabled, created_at
Rückgabe
Gibt 200 OK mit den Webhooks in data, mit next_page_url und previous_page_url (null am jeweiligen Ende) sowie einem Objekt usage zurück: used ist die Anzahl der Webhooks im Workspace, limit das Maximum Ihres Tarifs und filters_allowed gibt an, ob Ihr Tarif Payload-Filter enthält.
{
"data": [
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"url": "https://api.acme.com/webhooks/emailit",
"all_events": false,
"enabled": true,
"events": ["email.delivered", "email.bounced"],
"filter": null,
"filters_allowed": true,
"last_used_at": "2026-10-01T10:02:17.845000+00:00",
"created_at": "2026-10-01T09:41:05.302000+00:00",
"updated_at": "2026-10-01T10:02:17.845000+00:00"
}
],
"next_page_url": null,
"previous_page_url": null,
"usage": {
"used": 1,
"limit": 10,
"filters_allowed": true
}
}{
"statusCode": 401,
"error": "Unauthorized",
"message": "Invalid API key"
}Webhook löschen
Löscht einen Webhook und seine Event-Abonnements endgültig. Um Zustellungen vorübergehend zu stoppen, aktualisieren Sie den Webhook stattdessen mit enabled: false. Erfordert einen API-Schlüssel mit dem Scope full.
/webhooks/:idPfadparameter
idstringErforderlichID des Webhooks (wh_…) oder sein Name, URL-kodiert.
Rückgabe
Gibt 200 OK mit id und name des gelöschten Webhooks sowie deleted: true zurück. Gibt 404 mit error: "Webhook not found" zurück, wenn kein Webhook passt.
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"deleted": true
}{
"error": "Webhook not found"
}Test-Event senden
Sendet ein Beispiel-Event des gewählten Typs an die URL des Webhooks und gibt die Antwort Ihres Endpunkts zurück. So prüfen Sie, ob Ihr Endpunkt erreichbar ist und Signaturen korrekt verifiziert. Erfordert einen API-Schlüssel mit dem Scope full.
Die Anfrage hat dasselbe Format, dieselben Header und dieselbe Signatur wie eine echte Zustellung: ein JSON-Array mit einem Event, dessen event_id mit evt_test_ beginnt, signiert mit dem aktuellen Secret des Webhooks. Sie wird auch gesendet, wenn der Webhook deaktiviert ist oder diesen Typ nicht abonniert hat, wird nicht als Webhook-Anfrage gespeichert und nicht wiederholt. Die Beispieldaten sind fest vorgegeben und beziehen sich nicht auf echte Objekte.
Sie können 5 Test-Events pro Minute von derselben IP-Adresse senden; weitere geben 429 zurück.
/webhooks/{id}/testPfadparameter
idstringerforderlichwh_…) oder sein Name.Body-Parameter
typestringerforderlich| Ressource | Event-Typen |
|---|---|
email.accepted, email.scheduled, email.delivered, email.bounced, email.attempted, email.failed, email.rejected, email.clicked, email.loaded, email.complained, email.received, email.suppressed, email.canceled, email.unsubscribed, email.resubscribed |
|
| Domain | domain.created, domain.updated, domain.deleted |
| Kontaktliste | audience.created, audience.updated, audience.deleted |
| Abonnent | subscriber.created, subscriber.updated, subscriber.deleted |
| Kontakt | contact.created, contact.updated, contact.deleted |
| Vorlage | template.created, template.updated, template.deleted |
| Sperrung | suppression.created, suppression.updated, suppression.deleted |
| E-Mail-Verifizierung | email_verification.created, email_verification.updated, email_verification_list.created, email_verification_list.updated |
| Kampagne | campaign.created, campaign.updated, campaign.deleted, campaign.scheduled, campaign.queued, campaign.sending, campaign.testing, campaign.sent, campaign.canceled, campaign.archived |
Was jedes Event bedeutet, erfahren Sie unter Event-Typen.
Rückgabe
okbooleantrue, wenn Ihr Endpunkt mit einem 2xx-Status geantwortet hat.status_codeinteger0, wenn Emailit keine Verbindung herstellen konnte, die Anfrage nach 30 Sekunden in ein Timeout lief, der Endpunkt umgeleitet hat (HTTP-Redirects werden nicht verfolgt) oder die URL auf eine blockierte Adresse zeigt.bodystringtypestringpayloadobject[]Gibt 400 zurück, wenn type fehlt oder unbekannt ist, 404, wenn der Webhook nicht existiert, und 429, wenn Sie das Testlimit überschreiten.
{
"ok": true,
"status_code": 200,
"type": "email.delivered",
"payload": [
{
"event_id": "evt_test_3G0pUekgBm1uIxi9m4C380H70Zq",
"type": "email.delivered",
"object": {
"id": "eml_test_001",
"email_id": 12345,
"message_id": "<test-token@mydomain.com>",
"from": "sender@mydomain.com",
"to": "recipient@example.com",
"subject": "Test email",
"status": "delivered",
"delivered_at": "2026-01-15T10:30:00.000Z"
},
"data": {
"object": {
"id": "eml_test_001",
"email_id": 12345,
"message_id": "<test-token@mydomain.com>",
"from": "sender@mydomain.com",
"to": "recipient@example.com",
"subject": "Test email",
"status": "delivered",
"delivered_at": "2026-01-15T10:30:00.000Z"
}
}
}
],
"body": "{\"received\":true}"
}{
"error": "Unknown event type"
}{
"statusCode": 429,
"error": "Too Many Requests",
"message": "Rate limit exceeded, retry in 1 minute"
}Signatur-Secret rotieren
Erzeugt ein neues Signatur-Secret für den Webhook und gibt es zurück. Erfordert einen API-Schlüssel mit dem Scope full.
Das alte Secret wird sofort nicht mehr verwendet: Jede nach der Rotation gesendete Anfrage, einschließlich Wiederholungen früherer Events, wird mit dem neuen Secret signiert. Es gibt keinen Übergangszeitraum; aktualisieren Sie das Secret in Ihrem Endpunkt also direkt nach der Rotation oder akzeptieren Sie während der Umstellung kurzzeitig beide Secrets. Siehe Webhook-Signaturen verifizieren.
/webhooks/{id}/reset-secretPfadparameter
idstringerforderlichwh_…) oder sein Name.Rückgabe
Gibt das Webhook-Objekt mit dem neuen secret zurück (whsec_ gefolgt von 64 Hexadezimalzeichen). Gibt 404 zurück, wenn der Webhook nicht existiert.
{
"object": "webhook",
"id": "wh_3NGa6raJ3s1VJ75lO9km6Dqg4cQ",
"name": "Order notifications",
"url": "https://acme.com/webhooks/emailit",
"all_events": false,
"enabled": true,
"events": ["email.delivered", "email.bounced", "email.complained"],
"filter": null,
"last_used_at": "2026-10-01T12:58:40.000000+00:00",
"created_at": "2026-08-14T09:12:03.000000+00:00",
"updated_at": "2026-10-01T13:20:11.000000+00:00",
"secret": "whsec_a0a593d006bdec5aff2f21e88afd543ba239431acbe7d8dc7a9723d76e0a64e2"
}{
"error": "Webhook not found"
}Fehlgeschlagene Anfragen wiederholen
Stellt jede Anfrage dieses Webhooks, die in den letzten 7 Tagen endgültig fehlgeschlagen ist, erneut zur Zustellung in die Warteschlange. Erfordert einen API-Schlüssel mit dem Scope full.
Eine Anfrage schlägt nach ihrer letzten automatischen Wiederholung endgültig fehl (11 Versuche über mehrere Tage; siehe Wiederholungen und Fehlschläge). Erneut gestellte Anfragen beginnen wieder mit dem vollständigen Wiederholungsplan und werden innerhalb von Sekunden zugestellt. Wird mindestens eine Anfrage in die Warteschlange gestellt und war der Webhook deaktiviert, etwa nach 3 Tagen durchgehender Fehlschläge, wird er wieder aktiviert.
Beheben Sie zuerst das Problem an Ihrem Endpunkt, sonst schlagen die Anfragen erneut fehl. Um eine einzelne Anfrage zu wiederholen, nutzen Sie Einzelne Anfrage wiederholen.
/webhooks/{id}/retry-failedPfadparameter
idstringerforderlichwh_…) oder sein Name.Rückgabe
retriedinteger0, wenn es nichts zu wiederholen gab; der Aktivierungsstatus des Webhooks ändert sich dann nicht.Gibt 404 zurück, wenn der Webhook nicht existiert.
{
"retried": 37
}{
"error": "Webhook not found"
}Einzelne Anfrage wiederholen
Stellt eine endgültig fehlgeschlagene Webhook-Anfrage mit einem neuen Wiederholungsplan erneut zur Zustellung in die Warteschlange. War der Webhook deaktiviert, wird er wieder aktiviert. Erfordert einen API-Schlüssel mit dem Scope full.
Nur Anfragen, deren automatische Wiederholungen ausgeschöpft sind, lassen sich auf diese Weise wiederholen; für noch ausstehende oder gerade wiederholte Anfragen gibt der Endpunkt 400 zurück. Anfrage-IDs (whr_…) finden Sie im Tab Requests des Webhooks unter Email APIWebhooks. Um alle Anfragen der letzten 7 Tage auf einmal zu wiederholen, nutzen Sie Fehlgeschlagene Anfragen wiederholen.
/webhooks/{id}/requests/{request_id}/retryPfadparameter
idstringerforderlichwh_…) oder sein Name.request_idstringerforderlichwhr_…).Rückgabe
retriedinteger1.idstring| Status | Wann |
|---|---|
400 |
Die Anfrage ist nicht endgültig fehlgeschlagen oder hat kein Event, das erneut gesendet werden kann. |
404 |
Der Webhook existiert nicht oder die Anfrage gehört nicht zu ihm. |
{
"retried": 1,
"id": "whr_3tWVVMd2eHv3R9B5DWTLtwwU05X"
}{
"error": "Only permanently failed requests can be retried"
}{
"error": "Webhook request not found"
}