Sur cette page

Documentation API

API de courriel

Du courriel transactionnel sur le même compte, la même clé et le même solde que vos SMS, avec des données stockées au Canada et les règles de la LCAP intégrées.

Démarrage rapide

Avec une clé de test, vous pouvez envoyer avant de configurer un domaine : envoyez depuis onboarding@test.honkio.ca vers l’une des adresses de test ci-dessous. Rien n’est livré et rien n’est facturé.

bash
curl -X POST https://api.honkio.ca/v1/emails \
  -H "Authorization: Bearer mk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Acme <onboarding@test.honkio.ca>",
    "to": "delivered@test.honkio.ca",
    "subject": "Hello from HonkIO",
    "html": "<p>It works.</p>"
  }'

# Response 201: { "id": "cm...", "status": "queued", "scheduled_at": null }

Le même envoi avec le SDK Node, @honkio/node. Chaque appel renvoie data ou error et ne lève jamais d’exception pour une erreur de l’API.

javascript
import { Honkio } from '@honkio/node'

const honkio = new Honkio(process.env.HONKIO_API_KEY)

const { data, error } = await honkio.emails.send({
  from: 'Acme <onboarding@test.honkio.ca>',
  to: 'delivered@test.honkio.ca',
  subject: 'Hello from HonkIO',
  html: '<p>It works.</p>',
})
if (error) console.error(error.name, error.message)
else console.log(data.id)

Ou avec fetch :

javascript
const res = await fetch('https://api.honkio.ca/v1/emails', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.HONKIO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    from: 'Acme <onboarding@test.honkio.ca>',
    to: 'delivered@test.honkio.ca',
    subject: 'Hello from HonkIO',
    html: '<p>It works.</p>',
  }),
})
console.log(res.status, await res.json())

Ajoutez ensuite votre propre domaine, passez à une clé réelle et envoyez depuis une adresse de ce domaine.

Domaines d’envoi

Ajoutez un domaine ou un sous-domaine que vous contrôlez, puis publiez chez votre hébergeur DNS les enregistrements que renvoie l’API. Rien n’est placé sur votre domaine principal : votre propre courrier et votre enregistrement SPF restent intacts.

EnregistrementTypeNom (exemple)Objet
DKIMTXThonkio1._domainkey.mail.acme.caSigne votre courrier. Obligatoire.
MXMXsend.mail.acme.caChemin de retour des rebonds, sur le sous-domaine send. Obligatoire.
SPFTXTsend.mail.acme.caAutorise le chemin de retour, sur le sous-domaine send. Obligatoire.
DMARCTXT_dmarc.mail.acme.caRecommandé, non vérifié. Commencez par p=none.
bash
curl -X POST https://api.honkio.ca/v1/email-domains \
  -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "mail.acme.ca" }'

# Publish the returned records at your DNS host, then check them:
curl -X POST https://api.honkio.ca/v1/email-domains/DOMAIN_ID/verify \
  -H "Authorization: Bearer mk_live_YOUR_KEY"
# → { "status": "verified", "verified": true, "missing": [] }

# Tracking per domain (null = the platform default):
curl -X PATCH https://api.honkio.ca/v1/email-domains/DOMAIN_ID \
  -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "open_tracking": false, "click_tracking": null }'

Un domaine est en attente tant que ses enregistrements sont introuvables, en vérification pendant leur propagation, et vérifié quand DKIM, le MX, SPF et le fournisseur de courriel concordent. Il passe à échoué quand ses enregistrements sont encore manquants après 7 jours ou que le fournisseur de courriel rejette carrément le domaine, et à temporairement échoué après une défaillance passagère du fournisseur, ou quand un domaine déjà vérifié échoue à une nouvelle vérification ; appelez verify de nouveau une fois les enregistrements corrigés. La vérification est refaite chaque jour ; si un enregistrement disparaît, le domaine a 72 heures pour se rétablir avant de cesser d’envoyer.

Appelez verify à tout moment pour vérifier sur-le-champ. La réponse nomme tout enregistrement encore manquant.

Un domaine appartient à qui prouve qu’il contrôle son DNS. Le domaine vérifié d’un autre compte ne peut pas être ajouté ; une revendication non vérifiée bloque les autres pendant 72 heures, et la réponse 409 EMAIL_DOMAIN_IN_USE indique quand elle expire.

Le suivi des ouvertures et des clics est désactivé par défaut. Activez-le par domaine avec PATCH, ou par courriel avec le champ tracking ; null rétablit la valeur par défaut de la plateforme.

Une clé API peut être restreinte à certains domaines d’envoi dès sa création. Une clé restreinte ne peut envoyer que depuis ses propres domaines, et ne peut reprogrammer ou annuler qu’un courriel envoyé depuis l’un d’eux ; l’ajout, la vérification et la gestion des domaines eux-mêmes ne sont pas touchés et restent ouverts à toute clé disposant de la permission email_domains.

Envoyer un courriel

POST /v1/emails envoie un courriel. Les noms de champs sont ceux de Resend : la plupart des requêtes existantes fonctionnent telles quelles.

ChampSignification
fromExpéditeur, avec un nom au besoin. Son domaine doit être un domaine d’envoi vérifié de votre compte.
to, cc, bccUne chaîne ou un tableau ; au plus 50 destinataires entre to, cc et bcc.
reply_toUne chaîne ou un tableau.
subject, html, textObjet de 998 caractères au plus, et au moins html ou text. Omettez les trois pour envoyer un modèle.
variablesValeurs à substituer dans subject, html et text. Ignoré quand template est présent ; les variables propres à un modèle vont dans template.variables.
templateEnvoie un modèle enregistré par identifiant ou alias, avec ses variables.
attachmentsfilename et l’un de content (base64), content_base64 ou path (une URL HTTPS). content_id intègre le fichier dans le corps. 10 Mo au total.
tagsJusqu’à 10 paires name et value, chaque partie de 1 à 256 lettres, chiffres, traits de soulignement ou traits d’union. Filtrez avec GET /v1/emails?tag=name:value. Les tags sont conservés aussi longtemps que le courriel, même après la purge de l’objet et du corps; n’y mettez aucun renseignement personnel. Le champ body_purged_at d’un courriel récupéré est nul tant que ce n’est pas fait.
headersEn-têtes de courriel supplémentaires, envoyés tels quels.
scheduled_atUne date et heure ISO 8601, de 1 minute à 30 jours à l’avance.
is_commercialFalse par défaut. True désigne un message commercial au sens de la LCAP ; voir plus bas.
trackingOuvertures et clics pour ce courriel, qui priment sur le réglage du domaine.
bash
curl -X POST https://api.honkio.ca/v1/emails \
  -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Idempotency-Key: order-1042-receipt" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Acme Receipts <receipts@mail.acme.ca>",
    "to": ["ada@example.com"],
    "reply_to": "help@acme.ca",
    "subject": "Your receipt for order 1042",
    "html": "<img src=\"cid:logo\"><p>Thanks for your order.</p>",
    "text": "Thanks for your order.",
    "tags": [{ "name": "kind", "value": "receipt" }],
    "attachments": [
      { "filename": "receipt.pdf", "path": "https://files.acme.ca/r/1042.pdf" },
      { "filename": "logo.png", "content": "iVBORw0KGgo...", "content_id": "logo" }
    ],
    "scheduled_at": "2026-10-01T13:00:00-04:00"
  }'

# Move it, or cancel it (refunded), while it is still scheduled:
curl -X PATCH https://api.honkio.ca/v1/emails/EMAIL_ID -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" -d '{ "scheduled_at": "2026-10-02T09:00:00-04:00" }'
curl -X DELETE https://api.honkio.ca/v1/emails/EMAIL_ID -H "Authorization: Bearer mk_live_YOUR_KEY"

Une pièce jointe path est récupérée une seule fois à l’envoi, en HTTPS seulement, depuis une adresse publique, en 10 secondes au plus. Si elle est inaccessible, l’envoi est refusé avec EMAIL_ATTACHMENT_FETCH_FAILED et rien n’est facturé. Les pièces jointes ne sont conservées que jusqu’à l’envoi du courriel.

Un courriel programmé peut être déplacé avec PATCH ou annulé avec DELETE tant qu’il est encore programmé ; l’annulation rembourse les frais. Les pièces jointes fonctionnent aussi pour les envois programmés.

Envoyez un en-tête Idempotency-Key pour tout ce qui peut être relancé. La même clé renvoie la réponse d’origine au lieu d’envoyer, et de facturer, deux fois.

Une relance qui arrive pendant que la première requête portant cette clé est encore en cours de facturation reçoit 409 EMAIL_IDEMPOTENCY_IN_PROGRESS. Réessayez la même clé sous peu plutôt que d’en changer.

Envois groupés

Envoyez un tableau JSON d’au plus 100 courriels indépendants. Chaque élément est vérifié avant tout envoi ; un élément invalide fait échouer tout l’appel, tandis qu’un refus de conformité, comme une adresse supprimée, ne rejette que cet élément et est listé avec son indice.

bash
curl -X POST https://api.honkio.ca/v1/emails/batch \
  -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    { "from": "noreply@mail.acme.ca", "to": "ada@example.com", "subject": "Your code", "text": "123456" },
    { "from": "noreply@mail.acme.ca", "to": "lin@example.com", "template": { "id": "welcome", "variables": { "first_name": "Lin" } } }
  ]'

# → 200 { "data": [{ "id": "cm..." }, { "id": "cm..." }], "batch_id": "...", "rejected": [] }

data ne liste que les courriels réellement envoyés ; il n’est donc pas aligné avec votre requête par position. Faites correspondre un rejet à son élément d’origine grâce au champ index de rejected.

Ou envoyez un même message à au plus 500 destinataires, sous forme d’objet avec une liste recipients, chacun avec ses propres variables. Les pièces jointes et la programmation par élément ne sont pas offertes en envoi groupé.

bash
curl -X POST https://api.honkio.ca/v1/emails/batch \
  -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "noreply@mail.acme.ca",
    "template": { "id": "shipping-update" },
    "recipients": [
      { "to": "ada@example.com", "variables": { "first_name": "Ada", "tracking": "1Z999" } },
      { "to": "lin@example.com", "variables": { "first_name": "Lin", "tracking": "1Z998" } }
    ]
  }'

Modèles

Les modèles utilisent des doubles accolades autour d’un nom de variable fait de lettres, de chiffres et de traits de soulignement. Les triples accolades sont acceptées et traitées de la même façon : les valeurs sont toujours échappées en HTML dans html. Il n’y a ni conditions ni boucles.

text
Subject: Your order has shipped, {{ first_name }}
<p>Hi {{ first_name }}, track it with {{ tracking }}.</p>

"variables": [
  { "key": "first_name", "fallback": "there" },
  { "key": "tracking" }
]

Chaque variable qu’utilise un modèle doit être déclarée, avec une valeur de repli au besoin. Un envoi qui laisse une variable sans valeur ni repli est refusé et liste les clés manquantes.

Les modifications sont enregistrées dans un brouillon. La publication fait du brouillon la version numérotée suivante, que les envois utilisent ensuite ; un retour arrière republie une ancienne version comme nouvelle version. Les courriels programmés gardent leur version. Envoyez par identifiant ou alias ; un from ou reply_to dans la requête prime sur celui du modèle.

bash
# Create a draft, publish it as version 1, send it by alias
curl -X POST https://api.honkio.ca/v1/email-templates -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Shipping update", "alias": "shipping-update", "subject": "Shipped, {{ first_name }}",
        "html": "<p>Track it with {{ tracking }}.</p>",
        "variables": [{ "key": "first_name", "fallback": "there" }, { "key": "tracking" }] }'
curl -X POST https://api.honkio.ca/v1/email-templates/shipping-update/publish -H "Authorization: Bearer mk_live_YOUR_KEY"

curl -X POST https://api.honkio.ca/v1/emails -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "from": "noreply@mail.acme.ca", "to": "ada@example.com",
        "template": { "id": "shipping-update", "variables": { "tracking": "1Z999" } } }'

# Roll back: copies version 1 into the draft and publishes it as a new version
curl -X POST https://api.honkio.ca/v1/email-templates/shipping-update/rollback -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" -d '{ "version": 1 }'

Mode test

Les clés de test n’envoient rien et ne facturent rien, mais font les mêmes vérifications et déclenchent les mêmes webhooks qu’une clé réelle. Envoyez depuis onboarding@test.honkio.ca, sans domaine, vers ces adresses :

AdresseRésultat
delivered@test.honkio.caLivré.
bounced@test.honkio.caRebond définitif ; l’adresse est ajoutée à vos suppressions.
complained@test.honkio.caLivré, puis une plainte pour pourriel ; l’adresse est ajoutée à vos suppressions et désabonnée.
delayed@test.honkio.caUn retard de livraison, puis livré.
*@test.honkio.caToute autre adresse, et tout destinataire ordinaire avec une clé de test, est livré.

L’issue spéciale est lue dans to seulement : la première adresse to qui correspond à l’un de ces noms locaux la détermine. cc et bcc ne sont jamais examinées, si bien que les destinataires qui s’y trouvent sont toujours livrés.

La suppression fonctionne comme en réel : un second envoi à bounced@test.honkio.ca est refusé avec EMAIL_SUPPRESSED. Ajoutez une étiquette, par exemple bounced+2@test.honkio.ca, pour obtenir une nouvelle adresse. Une clé réelle ne peut pas du tout envoyer à test.honkio.ca ; c’est refusé avec EMAIL_TEST_ADDRESS_LIVE_KEY.

Les courriels et les événements de webhook d’une clé de test portent livemode à false.

LCAP et courriel commercial

Le courriel est transactionnel par défaut : reçus, réinitialisations de mot de passe, alertes et avis de compte. Le courriel transactionnel n’exige aucun consentement enregistré.

Mettez is_commercial à true pour tout ce qui fait la promotion d’un produit ou d’un service. Un courriel commercial va à un seul destinataire, exige un consentement LCAP enregistré pour cette adresse, et porte un pied de page de désabonnement et un en-tête de désabonnement en un clic que les logiciels de courriel respectent. Enregistrez le consentement comme pour les SMS, avec email_address au lieu de phone_number.

Tout courriel de marketing doit définir is_commercial à true. Le champ vaut false par défaut, et un envoi transactionnel atteint aussi les personnes qui se sont désabonnées : un courriel promotionnel envoyé sans ce drapeau contournerait leur désabonnement et le consentement LCAP.

bash
curl -X POST https://api.honkio.ca/v1/compliance/consents \
  -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email_address": "ada@example.com",
    "consent_type": "express",
    "source_description": "Newsletter checkbox on acme.ca/signup"
  }'

# Then a commercial send to that one address:
curl -X POST https://api.honkio.ca/v1/emails -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "from": "news@mail.acme.ca", "to": "ada@example.com", "subject": "Fall sale",
        "html": "<p>20% off this week.</p>", "is_commercial": true }'

DELETE /v1/compliance/consents/email/:address révoque les consentements actifs de cette adresse (exige compliance:m ou compliance:d) ; elle répond 404 NOT_FOUND si aucun n’est actif. Les désabonnements et les suppressions sont distincts et ne sont pas touchés.

Ceci décrit comment l’API applique la LCAP ; ce n’est pas un avis juridique sur vos propres messages.

Suppressions

Les rebonds définitifs et les plaintes pour pourriel ajoutent automatiquement l’adresse à votre liste de suppression, et plus aucun courriel ne lui est envoyé. Un désabonnement l’ajoute aussi, mais bloque seulement les courriels commerciaux : les reçus, les réinitialisations de mot de passe et les autres courriels transactionnels continuent d’arriver. Une adresse que vous bloquez vous-même ne reçoit plus aucun courriel. Vous pouvez lister les suppressions, ou en retirer une lorsque la personne demande à recevoir de vos nouvelles.

bash
curl "https://api.honkio.ca/v1/email-suppressions?reason=hard_bounce" -H "Authorization: Bearer mk_live_YOUR_KEY"
curl -X POST https://api.honkio.ca/v1/email-suppressions -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" -d '{ "email_address": "ada@example.com", "reason": "manual" }'
curl -X DELETE https://api.honkio.ca/v1/email-suppressions/ada%40example.com -H "Authorization: Bearer mk_live_YOUR_KEY"

Webhooks de courriel

Abonnez-vous aux événements de courriel comme aux événements SMS. Les livraisons sont signées exactement comme décrit dans Vérifier les signatures.

Chaque évènement arrive sous forme d’un seul objet JSON avec les mêmes six champs de premier niveau. Seul data change d’un évènement à l’autre.

ChampSignification
idL’identifiant de l’évènement, un UUID. Il est le même à chaque tentative et à chaque rejeu de cet évènement : conservez-le et ignorez tout id déjà traité.
typeLe nom de l’évènement, par exemple message.received. La même valeur est envoyée dans l’en-tête X-HonkIO-Event.
createdLe moment de l’évènement, en ISO 8601 UTC avec millisecondes. Une nouvelle tentative ou un rejeu garde la valeur d’origine.
account_idLe compte HonkIO auquel appartient l’évènement.
livemodetrue quand l’action d’une clé réelle a causé l’évènement, false pour une simulation avec une clé de test. Les évènements de compte et de numéro de téléphone valent toujours true.
dataLes champs propres à l’évènement, décrits sous chaque évènement ci-dessous.

email.queued

Un courriel a été accepté et facturé, et il est en file, ou planifié si vous avez passé scheduled_at.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.queued",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "from": "receipts@mail.acme.ca",
      "to": [
        "ada@example.com"
      ],
      "subject": "Your receipt",
      "tags": [
        {
          "name": "category",
          "value": "receipt"
        }
      ],
      "status": "queued",
      "is_commercial": false
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.fromL’adresse d’expédition du courriel.
email.toLes adresses des destinataires, sous forme de liste.
email.subjectLa ligne d’objet.
email.tagsLes étiquettes posées sur le courriel, sous forme de liste de paires name et value ; vide s’il n’y en a pas.
email.statusqueued, ou scheduled quand le courriel attend scheduled_at. Absent pour les courriels envoyés en lot.
email.batch_idCourriels envoyés avec POST /v1/emails/batch seulement : le lot auquel appartient le courriel.
email.is_commercialSi vous avez marqué le courriel comme commercial (LCAP), ce qui décide du pied de page et de l’en-tête de désabonnement.

email.sent

Le fournisseur de courriel a accepté le message pour livraison.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.sent",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "ses_message_id": "0101019xxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-000000",
      "from": "receipts@mail.acme.ca",
      "to": [
        "ada@example.com"
      ],
      "subject": "Your receipt",
      "tags": []
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.fromL’adresse d’expédition du courriel.
email.toLes adresses des destinataires, sous forme de liste.
email.ses_message_idL’identifiant du message chez le fournisseur de courriel, pour les demandes de soutien. Présent sur les évènements que le fournisseur signale.
email.subjectLa ligne d’objet.
email.tagsLes étiquettes posées sur le courriel, sous forme de liste de paires name et value ; vide s’il n’y en a pas.

email.delivered

Le serveur de courriel du destinataire a accepté le message.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.delivered",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "to": [
        "ada@example.com"
      ],
      "from": "receipts@mail.acme.ca",
      "ses_message_id": "0101019xxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-000000",
      "tags": [
        {
          "name": "category",
          "value": "receipt"
        }
      ]
    },
    "details": {
      "delivered_at": "2026-10-01T17:00:03.512Z",
      "smtp_response": "250 2.0.0 OK",
      "processing_time_ms": 1204,
      "recipients": [
        "ada@example.com"
      ],
      "ses_message_id": "0101019xxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-000000"
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.toLes adresses des destinataires, sous forme de liste.
email.fromL’adresse d’expédition du courriel.
email.ses_message_idL’identifiant du message chez le fournisseur de courriel, pour les demandes de soutien. Présent sur les évènements que le fournisseur signale.
email.tagsLes étiquettes posées sur le courriel, sous forme de liste de paires name et value ; vide s’il n’y en a pas.
details.delivered_atLe moment où le serveur du destinataire a accepté le message, en ISO 8601 UTC.
details.smtp_responseLa réponse SMTP du serveur du destinataire, telle quelle.
details.processing_time_msLa durée de la livraison depuis l’acceptation, en millisecondes.
details.recipientsLes adresses de destinataires visées par ce rapport.
details.ses_message_idLe même identifiant de fournisseur que email.ses_message_id.

email.delivery_delayed

Le serveur du destinataire diffère le message ; la livraison est encore retentée. Plusieurs peuvent arriver pour un même courriel.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.delivery_delayed",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "to": [
        "ada@example.com"
      ],
      "from": "receipts@mail.acme.ca",
      "ses_message_id": "0101019xxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-000000",
      "tags": [
        {
          "name": "category",
          "value": "receipt"
        }
      ]
    },
    "details": {
      "delay_type": "MailboxFull",
      "expiration_time": "2026-10-02T17:00:00.000Z",
      "recipients": [
        {
          "email_address": "ada@example.com",
          "status": "4.2.2",
          "diagnostic_code": "smtp; 452 4.2.2 Mailbox full"
        }
      ],
      "ses_message_id": "0101019xxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-000000"
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.toLes adresses des destinataires, sous forme de liste.
email.fromL’adresse d’expédition du courriel.
email.ses_message_idL’identifiant du message chez le fournisseur de courriel, pour les demandes de soutien. Présent sur les évènements que le fournisseur signale.
email.tagsLes étiquettes posées sur le courriel, sous forme de liste de paires name et value ; vide s’il n’y en a pas.
details.delay_typeLe type de retard signalé par le fournisseur, par exemple MailboxFull, SpamDetected ou TransientCommunicationFailure.
details.expiration_timeLe moment où le fournisseur cessera de réessayer et fera rebondir le message, en ISO 8601 UTC.
details.recipientsLes destinataires dont la livraison est retardée, un objet chacun.
details.recipients[].email_addressL’adresse d’un destinataire retardé.
details.recipients[].statusLe code de statut SMTP pour ce destinataire.
details.recipients[].diagnostic_codeL’explication du serveur du destinataire, quand il en donne une.
details.ses_message_idLe même identifiant de fournisseur que email.ses_message_id.

email.bounced

Le message a rebondi. Un rebond permanent supprime l’adresse ; un rebond temporaire, non.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.bounced",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "to": [
        "ada@example.com"
      ],
      "from": "receipts@mail.acme.ca",
      "ses_message_id": "0101019xxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-000000",
      "tags": [
        {
          "name": "category",
          "value": "receipt"
        }
      ]
    },
    "details": {
      "bounce_type": "Permanent",
      "bounce_subtype": "General",
      "diagnostic_code": "smtp; 550 5.1.1 user unknown",
      "suppressed": true,
      "transient": false,
      "recipients": [
        "ada@example.com"
      ],
      "ses_message_id": "0101019xxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-000000"
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.toLes adresses des destinataires, sous forme de liste.
email.fromL’adresse d’expédition du courriel.
email.ses_message_idL’identifiant du message chez le fournisseur de courriel, pour les demandes de soutien. Présent sur les évènements que le fournisseur signale.
email.tagsLes étiquettes posées sur le courriel, sous forme de liste de paires name et value ; vide s’il n’y en a pas.
details.bounce_typePermanent, Transient ou Undetermined.
details.bounce_subtypeLa classification plus fine du fournisseur, par exemple General, NoEmail ou MailboxFull.
details.diagnostic_codeL’explication du serveur du destinataire pour le premier destinataire rebondi, quand il en donne une.
details.suppressedtrue quand l’adresse a été ajoutée à votre liste de suppression (tout rebond permanent).
details.transienttrue pour un rebond temporaire, qui ne supprime pas l’adresse.
details.recipientsLes adresses de destinataires visées par ce rapport.
details.ses_message_idLe même identifiant de fournisseur que email.ses_message_id.

email.complained

Un destinataire a signalé le message comme pourriel. L’adresse est supprimée et enregistrée comme désabonnement.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.complained",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "to": [
        "ada@example.com"
      ],
      "from": "receipts@mail.acme.ca",
      "ses_message_id": "0101019xxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-000000",
      "tags": [
        {
          "name": "category",
          "value": "receipt"
        }
      ]
    },
    "details": {
      "complained_at": "2026-10-01T18:12:00.000Z",
      "recipients": [
        "ada@example.com"
      ],
      "ses_message_id": "0101019xxxxxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-000000"
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.toLes adresses des destinataires, sous forme de liste.
email.fromL’adresse d’expédition du courriel.
email.ses_message_idL’identifiant du message chez le fournisseur de courriel, pour les demandes de soutien. Présent sur les évènements que le fournisseur signale.
email.tagsLes étiquettes posées sur le courriel, sous forme de liste de paires name et value ; vide s’il n’y en a pas.
details.complained_atLe moment de la plainte, en ISO 8601 UTC.
details.recipientsLes adresses de destinataires visées par ce rapport.
details.ses_message_idLe même identifiant de fournisseur que email.ses_message_id.

email.rejected

Le message a été refusé avant ou pendant l’envoi, par les vérifications de HonkIO ou par le fournisseur de courriel, et les frais sont remboursés. Un refus de nos vérifications porte failure_code et failure_message ; un refus signalé par le fournisseur porte reason.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.rejected",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "from": "receipts@mail.acme.ca",
      "to": [
        "ada@example.com"
      ]
    },
    "details": {
      "failure_code": "DOMAIN_NOT_VERIFIED",
      "failure_message": "The sending domain is not verified."
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.fromL’adresse d’expédition du courriel.
email.toLes adresses des destinataires, sous forme de liste.
details.failure_codeLe code HonkIO qui explique le refus ou l’échec du courriel.
details.failure_messageUne explication lisible de failure_code.
details.reasonRefus signalés par le fournisseur de courriel seulement (à la place de failure_code) : la raison du fournisseur.
email.ses_message_idL’identifiant du message chez le fournisseur de courriel, pour les demandes de soutien. Présent sur les évènements que le fournisseur signale.

email.failed

Le courriel n’a pas pu être envoyé pour une raison qui ne tient pas au destinataire, comme une erreur de facturation ou du fournisseur, et les frais sont remboursés.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.failed",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "from": "receipts@mail.acme.ca",
      "to": [
        "ada@example.com"
      ]
    },
    "details": {
      "failure_code": "SES_ERROR",
      "failure_message": "The provider did not accept the message."
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.fromL’adresse d’expédition du courriel.
email.toLes adresses des destinataires, sous forme de liste.
details.failure_codeLe code HonkIO qui explique le refus ou l’échec du courriel.
details.failure_messageUne explication lisible de failure_code.

email.opened

La première ouverture du courriel : le pixel de suivi s’est chargé. Seulement quand le suivi des ouvertures est activé ; certains logiciels chargent les images automatiquement, une ouverture est donc un indice, pas une preuve. Les ouvertures suivantes ne déclenchent aucun webhook.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.opened",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "from": "receipts@mail.acme.ca",
      "to": [
        "ada@example.com"
      ]
    },
    "details": {
      "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)"
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.fromL’adresse d’expédition du courriel.
email.toLes adresses des destinataires, sous forme de liste.
details.uaL’agent utilisateur qui a chargé le pixel ou suivi le lien, ou null.

email.clicked

Le premier clic sur un lien suivi du courriel, quand le suivi des clics est activé. Les clics suivants ne déclenchent aucun webhook.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.clicked",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "from": "receipts@mail.acme.ca",
      "to": [
        "ada@example.com"
      ]
    },
    "details": {
      "url": "https://acme.ca/orders/1042",
      "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5)"
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.fromL’adresse d’expédition du courriel.
email.toLes adresses des destinataires, sous forme de liste.
details.urlLe lien suivi, tel qu’écrit dans votre courriel.
details.uaL’agent utilisateur qui a chargé le pixel ou suivi le lien, ou null.

email.unsubscribed

Un destinataire d’un courriel commercial s’est désabonné par son lien ou son en-tête, et il est maintenant supprimé pour ce domaine d’envoi.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.unsubscribed",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "from": "receipts@mail.acme.ca",
      "to": [
        "ada@example.com"
      ]
    },
    "details": {
      "recipient": "ada@example.com",
      "from_domain": "mail.acme.ca"
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.fromL’adresse d’expédition du courriel.
email.toLes adresses des destinataires, sous forme de liste.
details.recipientL’adresse qui s’est désabonnée.
details.from_domainLe domaine d’envoi pour lequel l’adresse est maintenant supprimée.

email.cancelled

Un courriel planifié a été annulé avant son envoi, et les frais ont été remboursés.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.cancelled",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "from": "receipts@mail.acme.ca",
      "to": [
        "ada@example.com"
      ]
    },
    "details": {
      "cancelled_at": "2026-10-01T16:58:00.000Z"
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.fromL’adresse d’expédition du courriel.
email.toLes adresses des destinataires, sous forme de liste.
details.cancelled_atLe moment de l’annulation, en ISO 8601 UTC.

email.rescheduled

Un courriel planifié a été déplacé à une nouvelle heure.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email.rescheduled",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "email": {
      "id": "clxxxemailxxxxxxxxxxxxxxx",
      "from": "receipts@mail.acme.ca",
      "to": [
        "ada@example.com"
      ],
      "tags": []
    },
    "details": {
      "previous_scheduled_at": "2026-10-02T13:00:00.000Z",
      "scheduled_at": "2026-10-03T13:00:00.000Z"
    }
  }
}
Champ de dataSignification
email.idL’identifiant HonkIO du courriel, tel que renvoyé par POST /v1/emails.
email.fromL’adresse d’expédition du courriel.
email.toLes adresses des destinataires, sous forme de liste.
email.tagsLes étiquettes posées sur le courriel, sous forme de liste de paires name et value ; vide s’il n’y en a pas.
details.previous_scheduled_atL’heure planifiée précédente, en ISO 8601 UTC.
details.scheduled_atLa nouvelle heure, en ISO 8601 UTC.

email_domain.verified

Les enregistrements DNS d’un domaine d’envoi sont validés et il peut envoyer.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email_domain.verified",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "domain": {
      "id": "clxxxdomainxxxxxxxxxxxxxx",
      "domain": "mail.acme.ca",
      "region": "ca-central-1"
    }
  }
}
Champ de dataSignification
domain.idL’identifiant HonkIO du domaine de courriel.
domain.domainLe nom de domaine.
domain.regionLa région d’où le domaine envoie (ca-central-1).

email_domain.verification_failed

Un domaine d’envoi a échoué la vérification, ou l’a perdue lors d’une vérification ultérieure de ses enregistrements.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "email_domain.verification_failed",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "domain": {
      "id": "clxxxdomainxxxxxxxxxxxxxx",
      "domain": "mail.acme.ca",
      "status": "temporarily_failed"
    },
    "details": {
      "failure_reason": "DKIM records not found",
      "revalidation": true
    }
  }
}
Champ de dataSignification
domain.idL’identifiant HonkIO du domaine de courriel.
domain.domainLe nom de domaine.
domain.statusfailed quand un autre compte a depuis vérifié le même domaine, temporarily_failed quand une nouvelle vérification d’un domaine déjà vérifié a échoué ; absent quand une première vérification a échoué.
details.failure_reasonPourquoi la vérification a échoué, par exemple quels enregistrements sont introuvables.
details.revalidationtrue quand il s’agissait d’une nouvelle vérification d’un domaine déjà vérifié. Absent sinon.

Chaque événement porte livemode : true pour une clé réelle, false pour une clé de test.

Pause automatique

Tous les courriels partent sous une même réputation d’envoi : un compte dont les courriels réels récents rebondissent ou suscitent des plaintes à un taux élevé voit son envoi réel de courriels suspendu 24 heures. Aujourd’hui, c’est plus de 5 % de rebonds définitifs sur ses 200 derniers courriels réels (dès qu’il en a envoyé au moins 50), ou plus de 0,1 % de plaintes sur ses 1 000 derniers, avec au moins 2 plaintes : une seule plainte ne suspend jamais. Pendant la pause, les envois réels répondent 403 SENDING_PAUSED avec channel email, les courriels programmés attendent et partent après la pause, et les SMS et les clés de test ne sont pas touchés. Nous pouvons modifier ces seuils pour protéger la délivrabilité ; cette page indique les seuils en vigueur.

La pause émet account.sending_paused avec { channel: "email", paused_until, reason, bounce_rate_pct ou complaint_rate_pct }, et le titulaire du compte est avisé par courriel.

Erreurs de courriel

Les appels de courriel peuvent aussi répondre avec les codes généraux du tableau d’erreurs principal, comme VALIDATION_ERROR, INSUFFICIENT_BALANCE et NOT_FOUND.

HTTPCodeSignification
403EMAIL_DOMAIN_NOT_ALLOWEDLa clé API est restreinte à certains domaines d’envoi, et celui-ci n’en fait pas partie.
409EMAIL_DOMAIN_ALREADY_EXISTSLe domaine est déjà sur ce compte.
409EMAIL_DOMAIN_IN_USEUn autre compte détient ce domaine. details.retry_after indique quand une revendication non vérifiée expire ; un domaine vérifié reste à son propriétaire.
409EMAIL_DOMAIN_HAS_INFLIGHTLe domaine a encore des courriels en file, programmés ou en cours d’envoi.
409EMAIL_IDEMPOTENCY_IN_PROGRESSUne requête utilisant cette clé Idempotency-Key est encore en cours de traitement. Réessayez la même clé sous peu plutôt que d’en utiliser une nouvelle.
409EMAIL_TEMPLATE_ALIAS_TAKENUn autre modèle de ce compte utilise déjà cet alias.
409EMAIL_TEMPLATE_IN_USEUn courriel programmé utilise ce modèle. Annulez-le, ou attendez qu’il soit envoyé, puis supprimez le modèle.
413EMAIL_ATTACHMENT_TOO_LARGELes pièces jointes dépassent 10 Mo.
422EMAIL_INVALID_ADDRESSUne adresse n’est pas une adresse courriel valide.
422EMAIL_DOMAIN_NOT_VERIFIEDLe domaine de l’expéditeur n’est pas un domaine d’envoi vérifié de ce compte.
422EMAIL_DOMAIN_INVALIDNom de domaine invalide.
422EMAIL_TEST_ADDRESS_LIVE_KEYUne clé réelle ne peut pas envoyer à une adresse sur test.honkio.ca ; ces adresses sont réservées aux clés de test.
422EMAIL_HEADER_INVALIDL’adresse d’expéditeur ou un en-tête est mal formé, ou est un en-tête que honkio définit lui-même.
422EMAIL_ATTACHMENT_FETCH_FAILEDUne pièce jointe fournie par path n’a pas pu être récupérée. details.reason indique pourquoi : blocked_address, http_status, timeout, too_large ou network.
422EMAIL_RENDER_FAILEDLe contenu n’a pas pu être généré.
422EMAIL_BATCH_TOO_LARGETrop de destinataires dans un seul appel.
422EMAIL_COMMERCIAL_MULTI_RECIPIENTUn courriel commercial va à un seul destinataire. Utilisez un envoi groupé pour en joindre plusieurs.
422EMAIL_NOT_SCHEDULEDSeul un courriel programmé peut être déplacé ou annulé.
422EMAIL_TEMPLATE_NOT_PUBLISHEDLe modèle n’a pas encore de version publiée. Publiez-le avant de l’utiliser pour un envoi.
422EMAIL_TEMPLATE_VARIABLE_MISSINGUne variable du modèle n’a ni valeur ni valeur de repli. details.keys énumère celles qui manquent.
422EMAIL_TEMPLATE_UNDECLARED_VARIABLELe corps du modèle utilise une variable qu’il ne déclare pas. Ajoutez-la à variables ; details.keys les énumère.
422EMAIL_TEMPLATE_LIMIT_REACHEDCe compte a atteint la limite de 200 modèles de courriel. Supprimez-en un pour en créer un autre.
451EMAIL_SUPPRESSEDLe destinataire figure sur votre liste de suppression.
451EMAIL_NO_CONSENTLe courriel commercial exige un consentement LCAP enregistré pour le destinataire.
451EMAIL_OPT_OUT_BLOCKEDLe destinataire s’est désabonné de cet expéditeur.
503EMAIL_ATTACHMENTS_UNCONFIGUREDLe stockage des pièces jointes n’est pas configuré, donc les courriels avec pièces jointes ne peuvent pas être envoyés pour le moment.

Codes d’échec de livraison

Ces codes ne sont jamais une réponse HTTP : l’appel a déjà répondu 201 avant que SES tente l’envoi. Si la livraison échoue quand même, l’un d’eux apparaît comme failure_code sur le webhook email.failed ou email.rejected, et les frais sont remboursés.

CodeSignification
SES_THROTTLINGSES a limité le débit d’envoi ; le courriel a échoué et a été remboursé ; renvoyez-le.
SES_MESSAGE_REJECTEDSES a rejeté le message d’emblée, par exemple un contenu mal formé ; le courriel a échoué et a été remboursé ; corrigez le contenu et renvoyez-le.
SES_DOMAIN_NOT_VERIFIEDSES n’avait pas terminé de vérifier le domaine d’expéditeur, même si la vérification de honkio avait réussi ; le courriel a échoué et a été remboursé ; attendez la fin de la vérification, puis renvoyez-le.
SES_ERRORUne erreur SES qui ne correspond à aucun autre code ; le courriel a échoué et a été remboursé ; renvoyez-le.
EMAIL_ATTACHMENTS_UNAVAILABLELes octets de la pièce jointe stockée n’ont pas pu être lus au moment de l’envoi ; le courriel a échoué et a été remboursé ; renvoyez-le.

Migrer depuis Resend

Pour la plupart des intégrations, changez l’URL de base et la clé. Avec le SDK, importez Honkio depuis @honkio/node au lieu de Resend.

ResendHonkIORemarques
https://api.resend.comhttps://api.honkio.ca/v1Les clés commencent par mk_live_ ou mk_test_.
from, to, cc, bcc, reply_toidentiqueAu plus 50 destinataires par appel.
subject, html, text, headersidentique
attachments[].content / path / filename / content_type / content_ididentiquecontent_base64 est accepté comme synonyme de content.
tags [{ name, value }]identique
scheduled_atscheduled_atISO 8601 seulement.
template { id, variables }identiqueLes modèles sont stockés dans HonkIO ; les triples accolades sont échappées.
POST /emails/batchPOST /v1/emails/batchTableau d’au plus 100, ou l’objet modèle pour au plus 500.
Idempotency-Keyidentique
reactnon offertGénérez d’abord votre courriel React en HTML.
audiences, broadcasts, inboundnon offert

Différences connues

  • scheduled_at prend une date et heure ISO 8601. Le langage naturel, comme in 1 hour, est refusé.
  • Les variables à triples accolades sont échappées en HTML, comme les doubles.
  • Le courriel commercial suit la LCAP : consentement enregistré, un seul destinataire, pied de page de désabonnement.
  • Les réponses gardent les champs propres à HonkIO, comme status, livemode et cost_cents, en plus de l’id que lisent les clients Resend.
  • Un envoi simple joint au plus 50 destinataires entre to, cc et bcc.