Courriel

Réception de courriel

La réception transforme une adresse en boîte de réception que votre compte lit par l’API, avec la même clé qui sert à envoyer.

Réception

La réception transforme une adresse en boîte de réception que votre compte peut lire par l’API. Le courriel qui lui est envoyé est analysé, stocké et renvoyé comme un enregistrement JSON : en-têtes, text, html, pièces jointes et les verdicts anti-pourriel et antivirus du fournisseur, récupéré avec la même clé qui sert à envoyer.

La réception réelle (l’adresse gérée et les domaines de réception) exige un numéro de mobile vérifié sur le compte, comme l’envoi réel; le mode test n’a pas cette exigence.

Chaque compte reçoit une adresse entrante gérée, sans rien à configurer : GET /v1/emails/received/address la renvoie, avec une adresse d’exemple construite à partir d’elle et si elle est réelle ou de test. N’importe quelle partie locale à cette adresse est acceptée, pour distribuer une adresse différente par client ou par commande sans rien créer d’abord.

L’adresse gérée a un interrupteur marche/arrêt : GET /v1/emails/received/address ajoute enabled, et PATCH /v1/emails/received/address avec { enabled } l’active ou la désactive. L’interrupteur vaut pour tout le compte et désactive votre adresse réelle, il exige donc une clé réelle : une clé de test reçoit 403 LIVE_KEY_REQUIRED, même si son GET indique toujours enabled. Tant qu’elle est désactivée, le courrier qui lui est envoyé est supprimé sans avertissement, sans frais, sans webhook email.received. Une simulation en mode test vers une adresse de test désactivée répond 422 INBOUND_ADDRESS_DISABLED.

Pour recevoir plutôt sur votre propre domaine, vérifiez-le d’abord pour l’envoi, puis activez la réception avec un PATCH vers /v1/email-domains/:id en mettant receiving à true. La réponse ajoute un receiving_status (off, pending, verified ou failed), l’enregistrement MX entrant à publier sur le domaine, et receiving_missing, qui liste cet enregistrement tant qu’il n’est pas publié ; rappelez verify une fois qu’il se résout, et son receiving_missing indique ce qu’il n’a toujours pas trouvé. Un domaine pas encore vérifié pour l’envoi répond 409 RECEIVING_REQUIRES_VERIFIED_DOMAIN. La réception ne reste active que tant que le domaine demeure vérifié pour l’envoi : si cette vérification tombe, receiving_status passe à failed, et le premier verify après que l’envoi est de nouveau vérifié la réactive.

const { data, error } = await honkio.domains.update('DOMAIN_ID', { receiving: true })
if (error) throw new Error(error.message)
console.log(data.domain, data.receiving_status, data.receiving_missing) // 'mail.acme.ca', 'pending', [...]
// Publish the MX record on the domain itself, then check it:
//   mail.acme.ca.  MX  10 inbound-smtp.ca-central-1.amazonaws.com.
const { data, error } = await honkio.domains.verify('DOMAIN_ID')
if (error) throw new Error(error.message)
console.log(data.receiving_status) // 'verified'

Nous recommandons de recevoir sur un sous-domaine, comme mail.acme.ca, plutôt que sur le domaine lui-même : d’autres outils qui envoient déjà du courriel depuis l’apex de votre domaine peuvent entrer en conflit avec l’enregistrement MX dont la réception a besoin là. Activez la réception sur l’apex quand même, et cela fonctionne toujours, la réponse portant receiving_warning à apex_mx en rappel.

email.received se déclenche dès qu’un message est accepté, mais ne porte que des métadonnées, pas le corps : récupérez l’enregistrement complet avec GET /v1/emails/received/:id une fois qu’il arrive, plutôt que de faire confiance au contenu du webhook. La livraison se fait au moins une fois : le même événement peut être livré plus d’une fois, dédupliquez donc sur data.email.id.

Le courriel accepté à une adresse où vous recevez est livré au moins une fois, même en cas de panne de notre côté : tout ce que notre point de réception manque est rejoué dans les minutes qui suivent son rétablissement, et une relecture n’est jamais facturée deux fois.

ChampSignification
GET /v1/emails/receivedListe le courriel reçu. Filtrez par to, from, since, until, domain_id et status ; paginez avec limit et cursor.
GET /v1/emails/received/addressL’adresse entrante gérée de ce compte, et si elle est réelle ou de test.
PATCH /v1/emails/received/addressActive ou désactive l’adresse entrante gérée avec { enabled }. Renvoie le même objet que le GET.
GET /v1/emails/received/:idL’enregistrement complet : chaque champ que la liste renvoie (id, from, from_name, to, cc, subject, message_id, in_reply_to, received_at, status, reject_reason, verdicts, attachments_count, size_bytes, charge_millicents, domain_id, livemode), plus text, html, headers, references, reply_to, envelope_recipients, attachments et attachments_bytes (leur taille totale). body_purged vaut true une fois le corps purgé, raw_available vaut false une fois le message brut expiré, et parse_failed vaut true quand le message n’a pas pu être analysé (téléchargez plutôt le message brut). body_truncated vaut true quand text ou html a été coupé à 2 Mo au stockage ; le message brut le conserve en entier. html_format=cid conserve les références cid: dans le html ; par défaut, elles sont réécrites en liens de pièce jointe. html_format=sanitized retire en plus les scripts et le balisage dangereux et met les images distantes de côté dans data-remote-src, comptées dans remote_images.
GET /v1/emails/received/:id/rawLe message d’origine, en message/rfc822.
GET /v1/emails/received/:id/attachments/:attachmentIdLes octets d’une pièce jointe.
const { data: address, error } = await honkio.emails.received.address()
if (error) throw new Error(error.message)
console.log(address.domain, address.example, address.enabled) // 'k7m2p9q4wx.inbound.honkio.ca', 'anything@k7m2p9q4wx.inbound.honkio.ca', true
const { data: address, error } = await honkio.emails.received.setAddressEnabled(false)
if (error) throw new Error(error.message)
console.log(address.enabled) // false
const { data, error } = await honkio.emails.received.list({ to: 'orders@mail.acme.ca', limit: 20 })
if (error) throw new Error(error.message)
for (const email of data.data) console.log(email.id, email.from, email.subject)
// sanitized strips scripts and unsafe markup from html and parks remote images
const { data: email, error } = await honkio.emails.received.get('EMAIL_ID', { htmlFormat: 'sanitized' })
if (error) throw new Error(error.message)
console.log(email.from, email.subject, email.message_id)

L’adresse gérée elle-même est gratuite : seuls les messages qu’elle accepte sont facturés. Dans le SDK Node.js, honkio.emails.received.address() la renvoie avec enabled, list et get renvoient le message analysé (en-têtes, text ou html, verdicts et métadonnées des pièces jointes), et raw() et attachment() renvoient un Blob plutôt que du JSON : lisez-le avec arrayBuffer() ou redirigez-le vers un fichier. get accepte une option htmlFormat, le html_format ci-dessus : links (par défaut), cid ou sanitized.

Chaque enregistrement porte des verdicts pour spf, dkim, dmarc, spam et virus, tels que le fournisseur les a jugés. Le courriel que son analyse marque comme virus est stocké avec status rejected et reject_reason virus, en-têtes seulement (sans corps ni pièces jointes), n’est jamais facturé et ne déclenche aucun webhook ; les autres sont informatifs, à votre propre filtrage d’en tenir compte.

Les pièces jointes et le message d’origine sont conservés 40 jours à partir de la réception ; chaque pièce jointe indique son propre expires_at. Après cela, les points de terminaison du message brut et des pièces jointes répondent 410 ATTACHMENT_EXPIRED, alors récupérez ce qu’il vous faut avant.

L’objet, le text, le html et les en-têtes d’un courriel sont purgés 90 jours après la réception, la même échéance que pour le courriel envoyé ; l’enregistrement lui-même, et ses métadonnées, restent, avec body_purged à true.

Recevoir coûte 0,0025 $ par message pour son premier Mo, puis 0,002 $ par Mo entamé du message entier (email_inbound_price_millicents et email_inbound_price_millicents_per_mb dans GET /v1/pricing), facturé une fois le message accepté. Le courriel bloqué comme virus, ou au-delà du plafond quotidien, n’est pas facturé.

Chaque compte peut recevoir au plus 1 000 messages par fenêtre glissante de 24 heures par défaut, un maximum relevé pour certains comptes par le soutien sur demande. Le courriel au-delà du plafond est stocké comme un rejet pour virus, avec status rejected et reject_reason daily_cap, en-têtes seulement, sans frais et sans webhook email.received (reject_reason vaut virus ou daily_cap). HonkIO déclenche account.inbound_email_capped et envoie un courriel au titulaire du compte une fois par fenêtre de 24 heures, pas à chaque message au-delà du plafond.

Pour garder une réponse dans le même fil dans le logiciel de courriel du destinataire, envoyez-la depuis un domaine que vous possédez, pas l’adresse entrante elle-même, par la table headers de l’API d’envoi : mettez le message_id du courriel reçu (le Message-ID RFC 5322, chevrons compris) dans un en-tête In-Reply-To, et ses references suivies de ce même message_id dans un en-tête References.

// The received email's message_id, e.g. "<abc123@example.com>", goes in In-Reply-To;
// References is its references followed by that same message_id.
const { data, error } = await honkio.emails.send({
  from: 'support@mail.acme.ca',
  to: 'ada@example.com',
  subject: 'Re: Where is my order?',
  text: 'It ships tomorrow.',
  headers: {
    'In-Reply-To': '<abc123@example.com>',
    References: '<abc123@example.com>',
  },
})
if (error) throw new Error(error.message)
console.log(data.id)

Les clés de test peuvent simuler un message entrant sans rien livrer réellement : POST /v1/emails/received/simulate prend from, to, cc, subject, et text ou html, plus en option attachments, verdicts, in_reply_to et headers. Chaque destinataire doit être votre adresse de test, celle que GET /v1/emails/received/address renvoie pour une clé de test (elle se termine par test-inbound.honkio.ca, pas inbound.honkio.ca), ou un domaine que ce compte possède en mode test. Il répond 403 TEST_KEY_REQUIRED pour une clé réelle, et 422 SIMULATE_RECIPIENT_NOT_OWNED autrement.

// With a test key (mk_test_YOUR_KEY): nothing is actually delivered.
const { data, error } = await honkio.emails.received.simulate({
  from: 'ada@example.com',
  to: ['orders@k7m2p9q4wx.test-inbound.honkio.ca'],
  subject: 'Where is my order?',
  text: 'Order 1042 has not arrived.',
})
if (error) throw new Error(error.message)
console.log(data.id, data.status) // 'rcv_...', 'received'