Endpunkte
`webhooks.list`, `list_all`, `iterate`, `get`, `create`, `update`, `delete`, `rotate_secret`, `test`, `get_delivery` und `replay_delivery` sowie die Zustell- und Aktivitätsprotokolle.
Jede Methode
from acme.secrets import store endpoint = client.webhooks.create({ 'url': 'https://acme.com/hooks/mail', 'eventTypes': ['email.sent', 'email.bounced'], 'description': 'Billing service',}) store(endpoint['secret']) client.webhooks.list()client.webhooks.get(endpoint['id'])client.webhooks.update(endpoint['id'], {'enabled': False})client.webhooks.test(endpoint['id'])latest = client.webhooks.list_deliveries(endpoint['id'], limit=1)['items'][0]client.webhooks.get_delivery(endpoint['id'], latest['id'])client.webhooks.replay_delivery(endpoint['id'], latest['id'])rotated = client.webhooks.rotate_secret(endpoint['id'])store(rotated['secret'])client.webhooks.delete(endpoint['id'])create ist, abgesehen von rotate_secret, der EINZIGE Zeitpunkt, an dem das Secret zurückgegeben wird. Ein Lesevorgang gibt es nie wieder aus; es sollte daher gespeichert werden, bevor irgendetwas anderes geschieht. Ohne eventTypes gilt die Standardauswahl, jedes email.*-Event außer email.replied. email.replied, domain.*, suppression.*, file.* und form.* erreichen einen Endpunkt nur, wenn er sie benennt.
rotate_secret hat kein Überlappungsfenster. Das alte Secret funktioniert sofort nicht mehr; das neue sollte daher vor der Rotation ausgerollt werden. Der Aufruf wird nie automatisch wiederholt: Ein Wiederholungsversuch würde ein zweites Mal rotieren und das Secret ungültig machen, das der erste Versuch zurückgegeben hat.
Was abonniert werden kann
WEBHOOK_EVENTS wird exportiert, damit sich die Liste rendern lässt. Events sind Events des **Postfachs**, nicht dieser API: email.received feuert für Mail, die in der App eingeht, und email.sent feuert für eine Nachricht, die der Composer gesendet hat. Ein Abonnement ist nicht dasselbe wie das Beobachten des eigenen API-Verkehrs.
file.uploaded feuert, wenn eine Datei auf der Seite „Dateien“ abgelegt wird, und file.deleted, wenn eine gelöscht wird. Ihre Daten sind FileEventData: fileId, filename, mimeType, sizeBytes, direction, to, threadId, messageId und uploadedAt oder deletedAt. to ist die Adresse, zu der die Datei gehört, oder null bei einer Datei, die zum ganzen Workspace gehört.
Die Datei-Events sind nicht in der Standardauswahl, ein Endpunkt erhält sie also nur, wenn er sie in eventTypes benennt. Ein auf bestimmte Adressen beschränkter Endpunkt erfährt nur von den Dateien dieser Adressen, ein Upload für den ganzen Workspace, mit to null, wird ihm also nicht gesendet.
form.submitted feuert, wenn sich jemand über eines Ihrer Formulare anmeldet, und form.confirmed, wenn eine ausstehende Anmeldung den Audiences beitritt, weil die Person den Bestätigungslink geöffnet hat oder weil Sie sie freigegeben haben. form.submitted trägt FormSubmittedEventData: formId, formName, submissionId, email, status, answers, audienceIds, sourceUrl und submittedAt. form.confirmed trägt FormConfirmedEventData: formId, formName, submissionId, email, audienceIds, via, das link oder approval ist, und confirmedAt.
Eine Anmeldung bei einem Formular ohne Double-Opt-in sendet form.submitted mit status added und kein form.confirmed; behandeln Sie diese Kombination also als den Moment, in dem jemand beitritt. Wer sich vor der Bestätigung erneut anmeldet, behält dieselbe submissionId, und form.submitted wird nur dann erneut gesendet, wenn sich die Antworten geändert haben. Die Formular-Events sind nicht in der Standardauswahl, und ein auf bestimmte Adressen beschränkter Endpunkt erhält sie nie, weil Anmeldungen dem ganzen Workspace gehören.
Jede dieser Datenformen ist ein TypedDict in openemail.types. Annotieren Sie ein verifiziertes Event etwa als WebhookPayload[FileEventData], und ein Typprüfer weiß, was event['data'] enthält.
Nachweisen, dass es funktioniert
result = client.webhooks.test('whe_…')delivery = result['delivery'] if delivery is not None: print(delivery['status'], delivery['responseCode']) for d in client.webhooks.iterate_deliveries('whe_…'): print(d['eventType'], d['status'], d['responseCode'], d['error'])Ein responseCode von None bedeutet, dass es überhaupt keine Antwort gab (DNS, TLS, ein Timeout), was etwas anderes ist als eine Antwort, die 0 lautete. Jede Zeile führt attempt und maxAttempts mit, sodass mehrere Zeilen ein Event beschreiben können: Die gleiche eventId über sie hinweg ist das Event, und die Versuchsnummer ist der Versuch. nextAttemptAt sagt, wann die automatische Wiederholung nach einer Zeile fällig ist.
Erneut senden
detail = client.webhooks.get_delivery('whe_…', 'whd_…')print(detail['payload'], detail['responseBody'], detail['replayRefusal']) replay = client.webhooks.replay_delivery('whe_…', 'whd_…')print(replay['delivery']['status'], replay['delivery']['responseCode'])Eine Zustellung, die immer wieder fehlschlägt, wird bis zu 8-mal versucht: sofort, dann nach 1 Minute, 5 Minuten, 30 Minuten, 2 Stunden, 5 Stunden, 10 Stunden und 10 Stunden, insgesamt etwa 27,5 Stunden. Wiederholt wird nur ein Fehlschlag, der eine Wiederholung wert ist: keine Antwort, 408, 425, 429 oder ein 5xx. Ein Replay sendet das gespeicherte Event erneut mit derselben id, demselben type, demselben createdAt und denselben data, sodass ein Empfänger, der bereits verarbeitete ids verwirft, es als das Event behandelt, das er schon kennt. Nur die Signatur ist neu.
replay_deliverysendet ein Event sofort und gibt zurück, was Ihr Server geantwortet hat. Es funktioniert auch bei einem zugestellten Versuch und wird nie wiederholt. Bevor es sendet, werden die automatischen Wiederholungen dieses Events angehalten, die noch nicht begonnen haben: Diese bleiben abgebrochen, wenn das Replay zugestellt wird, und laufen nach Plan weiter, wenn es fehlschlägt.- Wird in diesem Moment eine automatische Wiederholung desselben Events gesendet, sendet
replay_deliverynichts und wird mit 409retry_in_progressabgelehnt, und solange ein anderes Replay davon noch gesendet wird, wird es mit 409replay_in_progressabgelehnt, sodass Ihr Empfänger nie zwei Kopien gleichzeitig bekommt, auch nicht von zwei im selben Augenblick gesendeten Replays. Warten Sie ein paar Sekunden und lesen Sieget_delivery, denn diese Wiederholung oder dieses Replay kann es zustellen. Ein Replay betrifft immer genau ein Event: Kein Aufruf sendet alle fehlgeschlagenen Zustellungen erneut. - Außerdem lehnt es mit 409 einen ausgeschalteten Endpunkt ab (
webhook_disabled), ein Event, auf das der Endpunkt nicht mehr hört (event_not_subscribed) oder das er nicht mehr abdeckt (event_out_of_scope), und einen Versuch ohne gespeichertes Event (delivery_not_replayable).get_deliverymeldet diese Antwort vorab alsreplayRefusal.
Das SDK wiederholt replay_delivery nie von sich aus, weil eine Wiederholung nach einer verlorenen Antwort das Event noch einmal senden würde.
Jede Ablehnung löst OpenEmailApiError aus, mit status 409, is_conflict gleich true und dem Grund als code, einem der Werte in WEBHOOK_REPLAY_ERROR_CODES.
Parameter: webhooks.create
urlstrerforderlich- Wohin Zustellungen ge-POSTet werden. Nur HTTPS, und der Host darf nicht `localhost`, ein `.localhost`/`.local`/`.internal`-Name oder ein Loopback-, privates, CGNAT- oder Link-Local-IP-Literal sein. Dies ist ein serverseitiger fetch an eine selbst angegebene Adresse, daher ergeben diese ein 422 auf `url`; die Prüfung liest den Hostnamen so, wie er geschrieben steht, und löst nie DNS auf. Gespeichert wird die Serialisierung des URL-Parsers für das Gesendete, `https://acme.com` liest sich also als `https://acme.com/` zurück.
eventTypeslist[WebhookEvent]- Welche Events diesen Endpunkt erreichen: beliebige der Namen aus `WEBHOOK_EVENTS`. `POST /webhooks` begrenzt das Array auf die Anzahl der existierenden Events, eines mehr ist also ein 422 auf `eventTypes`; `PATCH` begrenzt es nicht. Begrenzt wird nur die Länge, und ein wiederholter Name wird genau so gespeichert und zurückgelesen, wie er gesendet wurde. Weggelassen oder leer wird als leere Liste gespeichert, weshalb sie sich als `['*']` zurückliest, und das bedeutet jedes `email.*`-Event außer `email.replied`, heute vierzehn, und nie die Domain-, Suppression- oder Datei-Familien. Eine später hinzugefügte Familie erreicht nie einen Endpunkt, der sie nicht benannt hat; eine Integration kann also nicht durch ein Release beginnen, eine Form zu empfangen, die sie nie gesehen hat.
descriptionstr- Eine Bezeichnung für den Endpunkt, höchstens 200 Zeichen, damit sich eine Liste von Webhooks als Namen liest und nicht als Spalte von URLs. Ohne Angabe wird sie als null gespeichert und zurückgegeben.
Antwort: CreatedWebhookResource
objectLiteral['webhook']- Immer `'webhook'`, derselbe Diskriminator, den ein einfaches Lesen zurückgibt, denn das Secret ist ein zusätzlicher Key auf der gewöhnlichen Form und kein eigener Objekttyp. Ob `secret` vorhanden ist, entscheidet die aufgerufene Methode und nicht dieses Feld.
idstr- Der Bezeichner des Endpunkts: `whe_` gefolgt von 24 Hexadezimalzeichen. Jeder andere Webhook-Aufruf nimmt ihn entgegen: `get`, `update`, `delete`, `rotate_secret`, `test`, `list_deliveries`, `list_all_deliveries`, `iterate_deliveries`, `get_delivery` und `replay_delivery`.
urlstr- Der Endpunkt, wie er gespeichert ist, nachdem er die HTTPS- und Blocked-Host-Prüfungen bestanden hat. Es ist die erneut serialisierte, geparste URL; verglichen werden sollte daher mit diesem Wert und nicht mit dem gesendeten String.
descriptionstr | None- Die vergebene Bezeichnung, oder null, wenn keine vergeben wurde. Ein `update`, das ein ausdrückliches null sendet, setzt sie wieder auf null zurück.
eventTypeslist[WebhookEvent] | ['*']- Die abonnierten Events, oder `['*']`, wenn der Endpunkt keine benannt hat. `['*']` ist die Darstellung einer leer gespeicherten Liste beim Lesen und kann nicht zurückgesendet werden; es steht für die vierzehn Nachrichten-Events und nicht für den gesamten Katalog. `create` und `update` akzeptieren ausschließlich die wörtlichen Event-Namen.
enabledbool- Ob Zustellungen versucht werden; ein deaktivierter Endpunkt wird beim Verteilen von Events übersprungen und behält sein Secret und seine Zustellhistorie. Hier immer true, da `WebhookCreate` kein `enabled` hat und nur `WebhookPatch` eines besitzt.
lastDeliveryAtstr | None- ISO 8601-Zeitstempel des letzten Zustell-VERSUCHS, nicht des letzten Erfolgs. Er wird auch nach einem fehlgeschlagenen POST gesetzt und sagt damit, dass der Endpunkt versucht wurde; wie es ausging, sagt `list_deliveries`. Null bis zum ersten Versuch und daher bei `create` immer null.
createdAtstr- ISO 8601-Zeitstempel der Registrierung des Endpunkts. `list` gibt Endpunkte nach diesem Feld sortiert zurück, neueste zuerst.
secretstr- Der HMAC-SHA-256-Schlüssel, der die `X-OpenEmail-Signature` jeder Zustellung signiert: `whsec_` gefolgt von 32 zufälligen Bytes in base64url, und das, was an `verify_webhook_signature` übergeben wird. Wird von `create` und `rotate_secret` zurückgegeben und von nichts sonst. Ein Lesevorgang gibt ihn nie wieder aus, er sollte also jetzt gespeichert werden; ein verlorenes Secret lässt sich nur mit `rotate_secret` ersetzen, was das alte sofort ungültig macht.
Die Protokolle filtern
from datetime import datetime, timedelta, timezone failed = client.webhooks.list_workspace_deliveries( status='failed', since=datetime.now(timezone.utc) - timedelta(days=1),)print(len(failed['items'])) history = client.webhooks.list_activity('whe_…')print([(change['type'], change['actor']['label'] if change['actor'] else None) for change in history['items']])list_deliveries liest einen Endpunkt und list_workspace_deliveries jeden Endpunkt oder die, die endpoint_ids= nennt, und beide nehmen status=, since= und until=, die Filter des Zustellungen-Tabs der Konsole. list_activity und list_workspace_activity lesen das Audit-Protokoll: wer was angelegt, geändert, geschaltet, rotiert, getestet, erneut gesendet oder entfernt hat. Jede hat ein list_all_… und ein iterate_… daneben, und jede Zeile des Workspace-Protokolls trägt endpointId.
since= und until= nehmen ein datetime oder einen ISO-8601-String. Ein naives datetime wird als Ortszeit gelesen und in UTC umgerechnet, übergeben Sie daher ein zeitzonenbewusstes, wie oben.
Referenz
webhooks.list()Vollständige Referenzwebhooks.list_all()Vollständige Referenzwebhooks.iterate()Vollständige Referenzwebhooks.get()Vollständige Referenzwebhooks.create()Vollständige Referenzwebhooks.update()Vollständige Referenzwebhooks.delete()Vollständige Referenzwebhooks.rotate_secret()Vollständige Referenzwebhooks.test()Vollständige Referenzwebhooks.list_deliveries()Vollständige Referenzwebhooks.list_all_deliveries()Vollständige Referenzwebhooks.iterate_deliveries()Vollständige Referenzwebhooks.get_delivery()Vollständige Referenzwebhooks.replay_delivery()Vollständige Referenzwebhooks.list_workspace_deliveries()Vollständige Referenzwebhooks.list_all_workspace_deliveries()Vollständige Referenzwebhooks.iterate_workspace_deliveries()Vollständige Referenzwebhooks.list_activity()Vollständige Referenzwebhooks.list_all_activity()Vollständige Referenzwebhooks.iterate_activity()Vollständige Referenzwebhooks.list_workspace_activity()Vollständige Referenzwebhooks.list_all_workspace_activity()Vollständige Referenzwebhooks.iterate_workspace_activity()Vollständige Referenz