Aller à la documentation
Ruby

Points de terminaison

`webhooks.list`, `list_all`, `iterate`, `get`, `create`, `update`, `delete`, `rotate_secret`, `test`, `get_delivery` et `replay_delivery`, ainsi que les journaux de livraison et d'activité.

Toutes les méthodes

webhooks.rb
endpoint = client.webhooks.create(  url: "https://acme.com/hooks/mail",  eventTypes: ["email.sent", "email.bounced"],  description: "Billing service") File.write(".openemail-webhook-secret", endpoint[:secret]) client.webhooks.listclient.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.firstclient.webhooks.get_delivery(endpoint[:id], latest[:id])client.webhooks.replay_delivery(endpoint[:id], latest[:id])rotated = client.webhooks.rotate_secret(endpoint[:id])File.write(".openemail-webhook-secret", rotated[:secret])client.webhooks.delete(endpoint[:id])

create est le SEUL moment où le secret est renvoyé, à part rotate_secret. Une lecture ne le répète jamais : stockez-le avant toute autre chose. Omettez eventTypes pour l'ensemble par défaut, tous les événements email.* sauf email.replied. email.replied, domain.*, suppression.*, file.* et form.* n'atteignent un endpoint que s'il les nomme.

rotate_secret n'a aucune fenêtre de recouvrement. L'ancien secret cesse de fonctionner immédiatement : déployez le nouveau avant de faire la rotation. L'appel n'est jamais réessayé automatiquement, car un réessai ferait une seconde rotation et invaliderait le secret que la première tentative a renvoyé.

create n'est pas réessayé non plus : une panne réseau peut donc laisser un endpoint créé avec un secret que vous n'avez jamais vu. Consultez list avant de le recréer. Un espace de travail contient 10 endpoints par défaut, et le suivant au-delà de la limite donne un 422 workspace_limit_reached.

Ce à quoi vous pouvez vous abonner

OpenEmail::WEBHOOK_EVENTS est un Hash gelé de tous les noms d'événements, pour que vous puissiez afficher la liste sans requête, et webhooks.list_events renvoie les mêmes noms avec une phrase pour chacun, ainsi que les limites auxquelles un endpoint est soumis. Les événements sont ceux de la **boîte aux lettres**, pas de cette API : email.received se déclenche pour du courrier qui arrive dans l'application, et email.sent pour un message envoyé depuis le compositeur. S'abonner n'équivaut pas à observer votre propre trafic API.

file.uploaded se déclenche quand un fichier est ajouté à la page Fichiers, et file.deleted quand un fichier est supprimé. Leur data contient fileId, filename, mimeType, sizeBytes, direction, to, threadId, messageId, et uploadedAt ou deletedAt. to est l'adresse à laquelle appartient le fichier, ou nil pour un fichier qui appartient à tout l'espace de travail.

Les événements de fichier ne font pas partie de l'ensemble par défaut : un endpoint ne les reçoit que s'il les nomme dans eventTypes. Un endpoint limité à certaines adresses n'est informé que des fichiers de ces adresses : un fichier importé pour tout l'espace de travail, avec to à nil, ne lui est donc pas envoyé.

form.submitted se déclenche quand quelqu'un s'inscrit via l'un de vos formulaires, et form.confirmed quand une inscription en attente rejoint les audiences, parce que la personne a ouvert le lien de confirmation ou parce que vous l'avez approuvée. Le data de form.submitted contient formId, formName, submissionId, email, status, answers, audienceIds, sourceUrl et submittedAt. Le data de form.confirmed contient formId, formName, submissionId, email, audienceIds, via, qui vaut link ou approval, et confirmedAt.

Une inscription sur un formulaire sans double opt-in envoie form.submitted avec status à added et aucun form.confirmed : traitez donc ce couple comme le moment où quelqu'un rejoint les audiences. Une personne qui s'inscrit de nouveau avant de confirmer garde le même submissionId, et form.submitted n'est renvoyé que si ses réponses ont changé. Les événements de formulaire ne font pas partie de l'ensemble par défaut, et un endpoint limité à certaines adresses ne les reçoit jamais, car les inscriptions appartiennent à tout l'espace de travail.

Prouver que ça fonctionne

webhook_test.rb
result = client.webhooks.test("whe_3f9c2a7b1e4d8f60a5c7b92d")puts result.dig(:delivery, :status), result.dig(:delivery, :responseCode) client.webhooks.iterate_deliveries("whe_3f9c2a7b1e4d8f60a5c7b92d") do |delivery|  puts "#{delivery[:eventType]} #{delivery[:status]} #{delivery[:responseCode]} #{delivery[:error]}"end

test envoie en POST un événement email.sent synthétique et signé, puis attend la fin de la tentative. Il revient normalement quoi qu'ait répondu votre récepteur : branchez-vous donc sur delivery[:status], et non sur le fait que l'appel ait levé une erreur. Un 4xx est une réponse utile : l'URL est joignable et le refus vient de votre propre gestionnaire, souvent de sa vérification de signature.

Un responseCode à nil signifie qu'il n'y a eu aucune réponse (DNS, TLS, un dépassement de délai), ce qui n'est pas la même chose qu'une réponse valant 0. Chaque ligne porte attempt et maxAttempts : plusieurs lignes peuvent donc décrire un même événement. L'eventId commun à ces lignes désigne l'événement, et le numéro de tentative désigne l'essai. nextAttemptAt indique quand est prévue la nouvelle tentative automatique qui suit une ligne.

Renvoyer

webhook_replay.rb
detail = client.webhooks.get_delivery("whe_3f9c2a7b1e4d8f60a5c7b92d", "whd_8c1e4a7f2b9d3e6a0c5f1b28")p detail[:payload], detail[:responseBody], detail[:replayRefusal] replay = client.webhooks.replay_delivery("whe_3f9c2a7b1e4d8f60a5c7b92d", "whd_8c1e4a7f2b9d3e6a0c5f1b28")p replay.dig(:delivery, :status), replay.dig(:delivery, :responseCode)

Une livraison qui continue d'échouer est tentée jusqu'à 8 fois : au moment où elle se produit, puis après 1 minute, 5 minutes, 30 minutes, 2 heures, 5 heures, 10 heures et 10 heures, soit environ 27 heures et demie au total. Seul un échec qui vaut la peine d'être répété l'est : pas de réponse, 408, 425, 429 ou un 5xx. Un rejeu renvoie l'événement stocké avec les mêmes id, type, createdAt et data, si bien qu'un destinataire qui ignore les identifiants déjà traités le considère comme l'événement qu'il connaît. Seule la signature est nouvelle.

  • replay_delivery envoie un événement maintenant et renvoie ce que votre serveur a répondu. Cela fonctionne aussi sur une tentative livrée, et ce n'est jamais réessayé. Avant l'envoi, les nouvelles tentatives automatiques de cet événement qui n'ont pas commencé sont suspendues : elles restent annulées si le rejeu est livré, et reprennent selon leur calendrier s'il échoue.
  • Si une nouvelle tentative automatique du même événement est en cours d'envoi à ce moment-là, replay_delivery n'envoie rien et lève un 409 retry_in_progress, et tant qu'un autre rejeu de cet événement est encore en cours d'envoi, il lève un 409 replay_in_progress : votre récepteur ne reçoit donc jamais deux copies à la fois, même de deux rejeux envoyés au même instant. Attendez quelques secondes et lisez get_delivery, car cette nouvelle tentative ou ce rejeu peut le livrer. Le rejeu se fait un événement à la fois : aucun appel ne renvoie toutes les livraisons échouées.
  • Il lève aussi un 409 pour un endpoint désactivé (webhook_disabled), un événement que l'endpoint n'écoute plus (event_not_subscribed) ou ne couvre plus (event_out_of_scope), et une tentative sans événement stocké (delivery_not_replayable). get_delivery annonce cette réponse à l'avance sous la forme de replayRefusal.

La gem ne réessaie jamais replay_delivery d'elle-même, car un réessai après une réponse perdue renverrait l'événement.

Paramètres : webhooks.create

urlStringobligatoire
L'adresse où les livraisons sont envoyées en POST. HTTPS uniquement, et l'hôte ne peut pas être `localhost`, un nom en `.localhost`, `.local` ou `.internal`, ni une IP littérale de loopback, privée, CGNAT ou link-local. Il s'agit d'une requête côté serveur vers une adresse que vous fournissez : ces cas donnent donc un 422 `invalid_webhook_url` sur `url`. La vérification lit le nom d'hôte tel qu'il est écrit, et chaque livraison résout à nouveau l'hôte et refuse d'envoyer vers une adresse située dans l'une de ces plages. Les livraisons ne suivent jamais les redirections : enregistrez donc l'adresse finale. Ce qui est stocké est la sérialisation, par l'analyseur d'URL, de ce que vous avez envoyé : `https://acme.com` se relit donc `https://acme.com/`.
eventTypesArray<String>
Les événements qui atteignent cet endpoint : n'importe laquelle des valeurs de `OpenEmail::WEBHOOK_EVENTS`. `create` plafonne l'Array au nombre d'événements existants : un de plus donne un 422 sur `eventTypes`, et `update` ne le plafonne pas. Seule la longueur est plafonnée, et un nom répété est stocké puis relu exactement tel que vous l'avez envoyé. Omis ou vide, il est stocké comme une liste vide, d'où sa relecture sous la forme `["*"]`, et il signifie tous les événements `email.*` sauf `email.replied`, quatorze aujourd'hui, et jamais les familles de domaine, de suppression, de fichier ou de formulaire. Une famille ajoutée plus tard n'atteint jamais un endpoint qui ne l'a pas nommée : une intégration ne peut donc pas se mettre à recevoir, à cause d'une nouvelle version, une forme qu'elle n'a jamais vue.
descriptionString
Un libellé pour l'endpoint, de 200 caractères au plus, pour qu'une liste de webhooks se lise comme des noms plutôt que comme une colonne d'URL. S'il est omis, il est stocké et renvoyé comme nil.
addressAllowlistArray<String>
Les adresses individuelles dont cet endpoint est informé. Un événement est livré quand l'adresse qu'il concerne figure sur cette liste, ou quand son domaine figure dans `domainAllowlist`. Laissez les deux vides et l'endpoint est informé de toutes les adresses que possède l'espace de travail. 50 au maximum, et une adresse que cet espace de travail ne possède pas donne un 422 `invalid_parameter`.
domainAllowlistArray<String>
Les domaines entiers dont cet endpoint est informé, y compris les adresses qui leur sont ajoutées plus tard. Un domaine porte aussi ses propres événements `domain.*`. 25 au maximum.
api_keyString
Crée l'endpoint avec cette clé au lieu de celle du client.

Réponse : l'endpoint créé

Un Hash à clés Symbol. get, list et update renvoient la même forme sans secret.

objectString
Toujours `webhook`, le même discriminant que renvoie une simple lecture, car le secret n'est qu'une clé supplémentaire sur la forme ordinaire plutôt qu'un type d'objet à part. La présence de `secret` dépend de la méthode que vous avez appelée, pas de ce champ.
idString
L'identifiant de l'endpoint : `whe_` suivi de 24 caractères hexadécimaux. Tous les autres appels de webhook le prennent : `get`, `update`, `delete`, `rotate_secret`, `test`, `list_deliveries`, `list_all_deliveries`, `iterate_deliveries`, `get_delivery` et `replay_delivery`.
urlString
L'endpoint tel qu'il est stocké, une fois passés les contrôles HTTPS et d'hôtes interdits. C'est l'URL analysée puis re-sérialisée : comparez donc à cette valeur plutôt qu'à la String que vous avez envoyée.
descriptionString or nil
Le libellé que vous lui avez donné, ou nil si vous n'en avez pas donné. Un `update` qui envoie `description: nil` l'efface.
eventTypesArray<String>
Les événements souscrits, ou `["*"]` lorsque l'endpoint n'en a nommé aucun. `["*"]` est la façon dont une liste stockée vide est rendue à la lecture et ne peut pas être renvoyé. Cette valeur représente les quatorze événements de message et non le catalogue entier. `create` et `update` n'acceptent que les noms d'événements littéraux.
enabledBoolean
Indique si les livraisons sont tentées. Un endpoint désactivé est ignoré lors de la distribution des événements et conserve son secret et son historique de livraisons. Toujours true ici, puisque seul `update` prend `enabled`.
disabledAtString or nil
Quand le serveur a désactivé l'endpoint après 100 livraisons échouées d'affilée. nil tant qu'il est actif, et quand c'est vous qui l'avez désactivé.
disabledReasonString or nil
Pourquoi le serveur l'a désactivé. nil chaque fois que `disabledAt` vaut nil.
consecutiveFailuresInteger
Les livraisons échouées d'affilée. Tout événement livré le remet à 0, tout comme `update` avec `enabled: true`.
addressAllowlistArray<String>
Les adresses individuelles dont cet endpoint est informé.
domainAllowlistArray<String>
Les domaines entiers dont cet endpoint est informé. Deux listes vides signifient toutes les adresses que possède l'espace de travail.
lastDeliveryAtString or nil
Horodatage ISO 8601 de la dernière TENTATIVE de livraison, et non de la dernière réussite. Il est aussi posé après un POST en échec : il vous dit que l'endpoint a été sollicité, et `list_deliveries` vous dit comment cela s'est passé. nil jusqu'à la première tentative, donc toujours nil sur `create`.
createdAtString
Horodatage ISO 8601 de l'enregistrement de l'endpoint. `list` renvoie les endpoints du plus récent au plus ancien selon ce champ.
secretString
La clé HMAC-SHA-256 qui signe le `X-OpenEmail-Signature` de chaque livraison : `whsec_` suivi de 43 caractères base64url, et ce que vous passez à `OpenEmail.verify_webhook_signature`, préfixe compris. Renvoyée par `create` et `rotate_secret`, et par rien d'autre. Une lecture ne la répète jamais : stockez-la maintenant. Un secret perdu ne peut être remplacé qu'avec `rotate_secret`, qui invalide l'ancien immédiatement.

Filtrer les journaux

webhook_logs.rb
failed = client.webhooks.list_workspace_deliveries(status: "failed", since: Time.now - 86_400)p failed.items.map { |delivery| [delivery[:endpointId], delivery[:eventType], delivery[:responseCode]] } history = client.webhooks.list_activity("whe_3f9c2a7b1e4d8f60a5c7b92d")p history.items.map { |change| [change[:type], change.dig(:actor, :label)] }

list_deliveries lit un endpoint et list_workspace_deliveries tous les endpoints, ou ceux que nomme endpoint_ids:, et les deux prennent status:, since: et until:, les filtres de l'onglet Livraisons de la console. list_activity et list_workspace_activity lisent le journal d'audit : qui a créé, modifié, activé ou désactivé, renouvelé, testé, rejoué ou supprimé quoi. Chacune a une version list_all_ et une version iterate_ à côté, et chaque ligne du journal de l'espace de travail porte endpointId. webhooks.stats renvoie les chiffres de l'onglet Statistiques pour la période de votre choix.

since: et until: prennent un Time, un DateTime ou un instant ISO 8601 sous forme de String, et une Date Ruby signifie minuit UTC ce jour-là. until est un mot-clé de Ruby, mais il fonctionne comme argument nommé comme n'importe quel autre : list_deliveries(id, since: start, until: finish).