Documentation API

API SMS

Envoyez votre premier SMS canadien en moins de 5 minutes.

L’authentification, les clés API, la configuration et la signature des webhooks, les limites de débit, le SDK, le serveur MCP et les codes d’erreur généraux se trouvent sur la page Plateforme.

Démarrage rapide

  1. Créer un compte gratuit, puis ouvrez Clés API → Créer la clé dans le tableau de bord. La clé complète ne s’affiche qu’une fois, juste après sa création : copiez-la à ce moment.
  2. Commencez par une clé de test : elle fonctionne immédiatement, avant toute recharge, pour envoyer des messages de test et explorer l’API gratuitement.
  3. Rechargez votre solde par carte via Stripe. Les clés de production retournent 402 PAYMENT_REQUIRED jusqu’à la première recharge.
  4. Vérifiez votre numéro de téléphone en tant que propriétaire du compte. Un envoi réel nécessite un numéro de propriétaire vérifié.
  5. Achetez un numéro canadien pour l’envoi ; un envoi réel nécessite un numéro appartenant à votre compte.
  6. Enregistrez le consentement LCAP pour chaque numéro de téléphone que vous allez contacter.
  7. Envoyez votre premier message via l’API REST.

Numéros de téléphone

Recherchez les numéros canadiens disponibles, provisionnez-en un et utilisez-le comme champ from lors de l'envoi.

bash
curl https://api.honkio.ca/v1/phone-numbers/search?area_codes=416 \
  -H "Authorization: Bearer mk_live_YOUR_KEY"

# Response 200 (per result): what buying it charges now and monthly, in CAD cents
# { "phone_number": "+14165550100", "region": "Ontario",
#   "upfront_cost_cents": 250, "activation_fee_cents": 100, "monthly_cost_cents": 250, ... }

# Provision a number
curl -X POST https://api.honkio.ca/v1/phone-numbers \
  -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone_number": "+14165550100"}'

# Accounts hold a limited number of numbers. GET /v1/accounts/me reports
# phone_number_limit and phone_numbers_used: check them before buying, or
# handle the 403 NUMBER_LIMIT_REACHED that a purchase past the cap returns.

# Ask HonkIO staff to raise the limit. One request may be pending at a time.
curl -X POST https://api.honkio.ca/v1/phone-numbers/allowance-requests \
  -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"requested_limit": 10, "reason": "Onboarding three new clinics this quarter"}'

# Response 201: { "status": "PENDING", "requested_limit": 10, ... }
# You are emailed if it is approved; the decision also shows in the dashboard.

# Check on it
curl https://api.honkio.ca/v1/phone-numbers/allowance-requests \
  -H "Authorization: Bearer mk_live_YOUR_KEY"

Consentement LCAP (requis avant l'envoi)

En vertu de la LCAP, vous devez enregistrer le consentement avant d'envoyer un message commercial à tout destinataire. L'API bloquera les envois vers des numéros de téléphone sans consentement valide (HTTP 451).

bash
curl -X POST https://api.honkio.ca/v1/compliance/consents \
  -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+1613XXXXXXX",
    "consent_type": "express",
    "source_description": "Website opt-in form",
    "source_ip": "203.0.113.1"
  }'

Consentement tacite pour un client existant, le délai courant à partir de sa dernière transaction :

bash
curl -X POST https://api.honkio.ca/v1/compliance/consents \
  -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+1613XXXXXXX",
    "consent_type": "implied",
    "relationship_type": "purchase",
    "last_transaction_date": "2025-11-04"
  }'

# → { "status": "recorded", "phone_number": "+1613XXXXXXX", "expires_at": "2027-11-04T00:00:00.000Z" }

Le consentement exprès n'expire jamais. Le consentement tacite expire après 2 ans selon l'art. 10(9) de la LCAP. Fournissez last_transaction_date pour que le délai de deux ans coure à partir de la relation réelle plutôt que du jour de l'enregistrement, ou indiquez directement expires_at si vous avez déjà calculé l'expiration. La réponse renvoie expires_at pour que vous puissiez le vérifier.

Envoi de SMS

Envoyez un message en utilisant un numéro provisionné. L'API valide le numéro de destination canadien, vérifie le consentement LCAP avant la livraison (vérification LNNTE du CRTC à venir).

bash
# "from" is one of your HonkIO numbers; "to" is a real number you hold consent for
curl -X POST https://api.honkio.ca/v1/messages \
  -H "Authorization: Bearer mk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "+1416XXXXXXX",
    "to":   "+1613XXXXXXX",
    "body": "Hello from HonkIO! 🇨🇦"
  }'

Vérification de numéro de téléphone (OTP)

Utilisez l'API Verify pour confirmer la propriété d'un numéro de téléphone avant d'envoyer des messages commerciaux. Votre utilisateur final reçoit un code à usage unique par SMS; soumettez-le au point de terminaison de vérification pour confirmer.

bash
# Start a verification (sends OTP SMS)
curl -X POST https://api.honkio.ca/v1/verify   -H "Authorization: Bearer mk_live_YOUR_KEY"   -H "Content-Type: application/json"   -d '{
    "from": "+1416XXXXXXX",
    "to":   "+1613XXXXXXX",
    "code_length": 6,
    "ttl_minutes": 10,
    "app_name": "Acme"
  }'
# Response: { "id": "clxxx...", "status": "pending", "code_length": 6, ... }

# Check the code submitted by your user
curl -X POST https://api.honkio.ca/v1/verify/clxxx.../check   -H "Authorization: Bearer mk_live_YOUR_KEY"   -H "Content-Type: application/json"   -d '{ "code": "483721" }'
# Response 200: { "status": "verified", ... }
# Response 422: { "code": "VERIFICATION_INVALID_CODE", "attempts_remaining": 4 }

# Fetch status at any time
curl https://api.honkio.ca/v1/verify/clxxx...   -H "Authorization: Bearer mk_live_YOUR_KEY"

En mode test, le code est toujours composé de zéros selon la longueur choisie (ex. 000000 pour 6 chiffres). Aucun SMS n'est envoyé et rien n'est facturé. Chaque vérification retourne un champ « mode » valant « LIVE » ou « TEST » afin de distinguer une vérification simulée d'une vérification réelle.

Tarification

Les prix sont définis à l'exécution et peuvent changer sans nouvelle version : lisez-les plutôt que de les coder en dur. Tous les montants sont en cents CAD. L'envoi est facturé par partie SMS : message_cost_cents × le nombre de parties en lesquelles l'opérateur découpe le texte. Les parties sont comptées comme l'opérateur les compte : guillemets typographiques, tirets et points de suspension sont convertis en GSM-7 (160 caractères, puis 153 par partie), tandis que les émojis et la plupart des lettres accentuées imposent des parties Unicode (70, puis 67). Le montant est ajusté au décompte de l'opérateur après l'envoi. Un message refusé d'emblée par l'opérateur ne coûte rien, pas plus qu'un envoi vers un central réservé (555-XXXX et semblables), refusé ici ; un message accepté par l'opérateur mais non livré conserve ses frais. Un texte de plus de 10 parties est rejeté avec 422 MESSAGE_TOO_LONG avant toute facturation. verification_cost_cents couvre un OTP typique d'une seule partie ; un app_name long ou non GSM peut ajouter une partie. phone_number_activation_fee_cents est facturé une seule fois, avec le premier mois, pour chaque numéro provisionné, local ou sans frais, et n'est pas remboursé à la libération. inbound_message_cost_cents est facturé par partie pour chaque SMS reçu sur un numéro provisionné, quel que soit l'expéditeur ou l'opérateur, sauf les mots-clés STOP, START et HELP ; un message reçu est débité même si le solde passe sous zéro, ce qui suspend l'envoi jusqu'à la prochaine recharge.

bash
# Current prices, in CAD cents
curl https://api.honkio.ca/v1/pricing \
  -H "Authorization: Bearer mk_live_YOUR_KEY"

# Response 200:
# {
#   "message_cost_cents": 3,
#   "verification_upcharge_cents": 25,
#   "verification_cost_cents": 28,
#   "phone_number_upfront_cost_cents": 250,
#   "phone_number_monthly_cost_cents": 250,
#   "phone_number_activation_fee_cents": 100,
#   "inbound_message_cost_cents": 3
# }

Les requêtes en mode test sont tarifées de façon identique dans la réponse, mais ne sont jamais facturées : vous pouvez donc voir ce que coûterait une intégration sans rien dépenser.

Limites d’envoi

HonkIO est conçu pour la messagerie transactionnelle et relationnelle, pas pour les campagnes, et la délivrabilité de chaque client repose sur un profil opérateur partagé. Ces limites gardent le marketing de masse hors de la plateforme ; une clinique, un entrepreneur ou un SaaS qui envoie des codes ne les remarquera pas. Les limites d’envoi s’appliquent uniquement au mode réel ; la règle sur les raccourcisseurs de liens, les vérifications de destination réservée et injoignable, et le plafond de taille des diffusions s’appliquent aux deux modes.

  • Plafond quotidien : les nouveaux comptes peuvent envoyer 250 messages réels par 24 heures glissantes. Il ne se lève pas de lui-même. 30 jours après votre premier message réel, vous pouvez demander un volume plus élevé depuis le tableau de bord ; l’approbation fixe 1 000 par jour ou le chiffre demandé. Les envois refusés renvoient 429 DAILY_LIMIT_REACHED avec votre limite et votre compte.
  • Messages identiques : un même corps de message peut atteindre au plus 250 destinataires distincts par 24 heures (429 FANOUT_LIMIT_REACHED). Les messages personnalisés ne sont pas concernés.
  • Débit par numéro : 60 messages par minute par numéro d’envoi, ce que les opérateurs canadiens accordent de toute façon à un numéro long (429 NUMBER_RATE_LIMITED avec Retry-After).
  • Diffusions : jusqu’à 250 destinataires par diffusion de groupe et 3 diffusions par 24 heures (422 BROADCAST_TOO_LARGE, 429 BROADCAST_LIMIT_REACHED).
  • Les raccourcisseurs de liens (bit.ly, tinyurl et similaires) sont refusés dans les deux modes, car les opérateurs les filtrent (422 LINK_SHORTENER_BLOCKED). Utilisez l’URL complète.
  • Avertissement de livraison, puis pause automatique : si plus de 10 % de vos 50 derniers messages réels échouent chez l’opérateur, vous recevez un courriel (et l’événement account.delivery_warning) sans aucune pause. Si plus de 1 % des destinataires répondent STOP, ou plus de 5 % des messages sont rejetés par les opérateurs, sur vos envois récents, l’envoi réel est suspendu 24 heures et vous êtes avisé par courriel (403 SENDING_PAUSED avec l’heure de reprise ; l’événement account.sending_paused est émis).
  • Rechargements : le solde ne peut dépasser 500 $ et les rechargements sont limités à 1 000 $ par 30 jours. Relevés sur demande.
  • Plafond par destinataire : 30 messages vers un même destinataire par heure et 100 par 24 heures (429 RECIPIENT_RATE_LIMITED avec Retry-After). Une conversation bidirectionnelle n’en approche jamais ; un script qui réessaie le même numéro, oui.
  • Les messages non livrés sont facturés : un message accepté par l’opérateur mais non livré conserve ses frais. Un central réservé (555-XXXX, centraux N11 comme 411 ou 911, codes de test des opérateurs, centraux commençant par 0 ou 1) est refusé sans frais avec 422 RESERVED_DESTINATION, en mode test aussi, et un indicatif régional réservé (555, 911 et semblables) comme tout numéro non canadien. Tout ce qui réessaie devrait envoyer un en-tête Idempotency-Key afin qu’une nouvelle tentative ne devienne jamais un second débit. Une heure de dépenses inhabituelles déclenche un courriel et l’événement account.spend_warning.
  • Numéros injoignables : après 3 échecs consécutifs chez l’opérateur vers un même numéro en 30 jours, tous clients HonkIO confondus, les envois vers ce numéro sont refusés sans frais (422 UNDELIVERABLE_NUMBER avec le nombre d’échecs, la date d’inscription et la date d’expiration) pendant 90 jours, puis réessayés au cas où le numéro aurait été réattribué.
bash
curl https://api.honkio.ca/v1/send-limit \
  -H "Authorization: Bearer mk_live_YOUR_KEY"

# → { "daily_limit": 250, "sent_last_24h": 12, "remaining": 238,
#     "recipient_rate_per_hour": 30, "recipient_rate_per_day": 100,
#     "probation": { "ends_at": "2026-09-26T14:02:11.000Z", "eligible_to_request": false },
#     "paused_until": null, "requests": [] }

Consultez vos limites et votre utilisation avec GET /v1/send-limit, et déposez une demande avec POST /v1/send-limit/requests. Chaque chiffre ci-dessus est une valeur par défaut de la plateforme qui peut être relevée par compte.

Événements webhook

Voici les événements SMS, de désabonnement et de numéro de téléphone. L’inscription d’un point de terminaison, l’enveloppe de chaque événement, les nouvelles tentatives et les signatures sont décrites dans Webhooks de la plateforme.

Chaque charge utile est signée avec HMAC-SHA256 : vérifiez l’en-tête X-HonkIO-Signature. Un évènement message.received contient l’expéditeur, votre numéro, le texte, keyword_action (traitement STOP/START), ainsi que segment_count et cost_cents, le montant facturé pour ce message. Un évènement message.sent contient carrier_message_id, l’identifiant que l’opérateur a attribué au message, avec les deux mêmes champs de facturation.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "message.delivered",
  "created": "2026-09-19T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "message_id": "clxxxmessagexxxxxxxxxxxxx",
    "status": "delivered",
    "message_status": "DELIVERED",
    "to": "+1613XXXXXXX",
    "from": "+1416XXXXXXX",
    "errors": []
  }
}
// Headers: X-HonkIO-Signature, X-HonkIO-Timestamp, X-HonkIO-Event

Un même message_id peut émettre message.failed (seulement si l’échec était CARRIER_UNAVAILABLE ; un échec INSUFFICIENT_BALANCE n’émet aucun webhook) puis de nouveau message.queued, puis message.sent, lorsqu’une nouvelle tentative avec la même Idempotency-Key relance un message ayant échoué avant d’atteindre l’opérateur.

Chaque évènement, tel que votre point de terminaison le reçoit. Les exemples sont caviardés : identifiants, numéros et adresses sont fictifs. Des champs peuvent s’ajouter avec le temps, ignorez donc ceux que vous ne reconnaissez pas.

message.queued

Un message sortant a été accepté et facturé, et va être remis à l’opérateur.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "message.queued",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "message_id": "clxxxmessagexxxxxxxxxxxxx",
    "from": "+1416XXXXXXX",
    "to": "+1613XXXXXXX",
    "status": "QUEUED",
    "message_status": "QUEUED"
  }
}
Champ de dataSignification
message_idL’identifiant HonkIO du message, tel que renvoyé par POST /v1/messages ou listé par GET /v1/messages.
fromVotre numéro HonkIO d’où le message est parti, au format E.164.
toLe numéro du destinataire, au format E.164.
statusLe statut du message à ce moment, en majuscules (la même valeur que message_status).
message_statusLe statut HonkIO du message, en majuscules : QUEUED, SENDING, DELIVERED, FAILED, UNDELIVERED ou RECEIVED.

message.sending

Réservé : rarement, voire jamais envoyé. Il ne se déclenche que si un accusé final de l’opérateur indique le statut sending, ce qui n’est pas attendu en pratique.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "message.sending",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "message_id": "clxxxmessagexxxxxxxxxxxxx",
    "status": "sending",
    "message_status": "SENDING",
    "to": "+1613XXXXXXX",
    "from": "+1416XXXXXXX",
    "errors": []
  }
}
Champ de dataSignification
message_idL’identifiant HonkIO du message, tel que renvoyé par POST /v1/messages ou listé par GET /v1/messages.
statusLe statut propre à l’opérateur tiré de son accusé, en minuscules (par exemple delivered, sending_failed, delivery_failed ou expired).
message_statusLe statut HonkIO du message, en majuscules : QUEUED, SENDING, DELIVERED, FAILED, UNDELIVERED ou RECEIVED.
toLe numéro du destinataire, au format E.164.
fromVotre numéro HonkIO d’où le message est parti, au format E.164.
errorsCe que l’opérateur a signalé, sous forme de liste ; vide s’il n’a rien signalé.
errors[].codeLe code d’erreur de l’opérateur, tel quel.
errors[].titleUne brève description de l’erreur.
errors[].detailPlus de détails, quand l’opérateur en donne.

message.sent

L’opérateur a accepté le message pour livraison. Déclenché au retour de l’appel d’envoi, pas par un accusé.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "message.sent",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "message_id": "clxxxmessagexxxxxxxxxxxxx",
    "from": "+1416XXXXXXX",
    "to": "+1613XXXXXXX",
    "status": "SENDING",
    "message_status": "SENDING",
    "carrier_message_id": "40319xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "segment_count": 1,
    "cost_cents": 3
  }
}
Champ de dataSignification
message_idL’identifiant HonkIO du message, tel que renvoyé par POST /v1/messages ou listé par GET /v1/messages.
fromVotre numéro HonkIO d’où le message est parti, au format E.164.
toLe numéro du destinataire, au format E.164.
statusLe statut du message à ce moment, en majuscules (la même valeur que message_status).
message_statusLe statut HonkIO du message, en majuscules : QUEUED, SENDING, DELIVERED, FAILED, UNDELIVERED ou RECEIVED.
carrier_message_idL’identifiant attribué au message par l’opérateur, pour les demandes de soutien.
segment_countLe nombre de segments SMS du message, qui est la base de sa facturation.
cost_centsCe que le message vous a coûté, en cents canadiens.

message.delivered

L’accusé de l’opérateur indique que le message a atteint l’appareil.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "message.delivered",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "message_id": "clxxxmessagexxxxxxxxxxxxx",
    "status": "delivered",
    "message_status": "DELIVERED",
    "to": "+1613XXXXXXX",
    "from": "+1416XXXXXXX",
    "errors": []
  }
}
Champ de dataSignification
message_idL’identifiant HonkIO du message, tel que renvoyé par POST /v1/messages ou listé par GET /v1/messages.
statusLe statut propre à l’opérateur tiré de son accusé, en minuscules (par exemple delivered, sending_failed, delivery_failed ou expired).
message_statusLe statut HonkIO du message, en majuscules : QUEUED, SENDING, DELIVERED, FAILED, UNDELIVERED ou RECEIVED.
toLe numéro du destinataire, au format E.164.
fromVotre numéro HonkIO d’où le message est parti, au format E.164.
errorsCe que l’opérateur a signalé, sous forme de liste ; vide s’il n’a rien signalé.
errors[].codeLe code d’erreur de l’opérateur, tel quel.
errors[].titleUne brève description de l’erreur.
errors[].detailPlus de détails, quand l’opérateur en donne.

message.failed

Le message n’est pas parti. Trois formes selon l’endroit de l’échec : un échec au passage à l’opérateur (CARRIER_UNAVAILABLE, CARRIER_TIMEOUT, CARRIER_ERROR, INVALID_PHONE_NUMBER, NOT_A_MOBILE_NUMBER) porte error_code et error_message mais pas de tableau errors ; un accusé de l’opérateur porte son statut brut, un tableau errors et un error_code, mais pas de error_message ; un envoi interrompu par un redémarrage du serveur (error_code STALE_QUEUED) ne porte ni to ni from. Un échec pour INSUFFICIENT_BALANCE ne déclenche aucun webhook.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "message.failed",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "message_id": "clxxxmessagexxxxxxxxxxxxx",
    "from": "+1416XXXXXXX",
    "to": "+1613XXXXXXX",
    "status": "FAILED",
    "message_status": "FAILED",
    "error_code": "INVALID_PHONE_NUMBER",
    "error_message": "The destination is not a valid phone number."
  }
}
Champ de dataSignification
message_idL’identifiant HonkIO du message, tel que renvoyé par POST /v1/messages ou listé par GET /v1/messages.
statusLe statut propre à l’opérateur tiré de son accusé, en minuscules (par exemple delivered, sending_failed, delivery_failed ou expired).
message_statusLe statut HonkIO du message, en majuscules : QUEUED, SENDING, DELIVERED, FAILED, UNDELIVERED ou RECEIVED.
toLe numéro du destinataire, au format E.164.
fromVotre numéro HonkIO d’où le message est parti, au format E.164.
error_codeLe code d’erreur HonkIO de l’échec, le même que celui du message dans l’API.
error_messageUne explication lisible de error_code.
errorsCe que l’opérateur a signalé, sous forme de liste ; vide s’il n’a rien signalé.
errors[].codeLe code d’erreur de l’opérateur, tel quel.
errors[].titleUne brève description de l’erreur.
errors[].detailPlus de détails, quand l’opérateur en donne.

message.undelivered

L’opérateur a pris le message mais n’a pas pu le livrer : bloqué, expiré, ou appareil injoignable.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "message.undelivered",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "message_id": "clxxxmessagexxxxxxxxxxxxx",
    "status": "delivery_failed",
    "message_status": "UNDELIVERED",
    "to": "+1613XXXXXXX",
    "from": "+1416XXXXXXX",
    "errors": [
      {
        "code": "40002",
        "title": "Blocked as spam",
        "detail": "The destination carrier blocked the message."
      }
    ]
  }
}
Champ de dataSignification
message_idL’identifiant HonkIO du message, tel que renvoyé par POST /v1/messages ou listé par GET /v1/messages.
statusLe statut propre à l’opérateur tiré de son accusé, en minuscules (par exemple delivered, sending_failed, delivery_failed ou expired).
message_statusLe statut HonkIO du message, en majuscules : QUEUED, SENDING, DELIVERED, FAILED, UNDELIVERED ou RECEIVED.
toLe numéro du destinataire, au format E.164.
fromVotre numéro HonkIO d’où le message est parti, au format E.164.
errorsCe que l’opérateur a signalé, sous forme de liste ; vide s’il n’a rien signalé.
errors[].codeLe code d’erreur de l’opérateur, tel quel.
errors[].titleUne brève description de l’erreur.
errors[].detailPlus de détails, quand l’opérateur en donne.

message.received

Quelqu’un a texté l’un de vos numéros HonkIO. Les réponses STOP, START et HELP arrivent aussi ici, avec keyword_action qui indique ce qui en a été fait.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "message.received",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "message_id": "clxxxmessagexxxxxxxxxxxxx",
    "from": "+1613XXXXXXX",
    "to": "+1416XXXXXXX",
    "body": "Yes, see you at 3",
    "keyword_action": "ignored",
    "segment_count": 1,
    "cost_cents": 1,
    "message_status": "RECEIVED"
  }
}
Champ de dataSignification
message_idL’identifiant HonkIO du message, tel que renvoyé par POST /v1/messages ou listé par GET /v1/messages.
fromLe numéro qui vous a texté, au format E.164.
toVotre numéro HonkIO qui a reçu le texto, au format E.164.
bodyLe texte du message.
keyword_actionCe que HonkIO a fait d’un mot-clé de conformité, d’après le premier mot du texte : opted_out (STOP et semblables, suivi d’un évènement opt_out.recorded), reinstated (START ou UNSTOP après un désabonnement, suivi d’un évènement opt_out.reinstated), help (HELP, INFO ou AIDE ; la réponse automatique a été envoyée), ou ignored.
segment_countLe nombre de segments SMS du message, qui est la base de sa facturation.
cost_centsCe que la réception du message vous a coûté, en cents canadiens.
message_statusLe statut HonkIO du message, en majuscules : QUEUED, SENDING, DELIVERED, FAILED, UNDELIVERED ou RECEIVED.

opt_out.recorded

Un abonné a répondu à l’un de vos numéros par un mot-clé de désabonnement (une réponse dont le premier mot est STOP, STOPALL, UNSUBSCRIBE, CANCEL, END ou QUIT), et les messages vers lui depuis ce numéro sont maintenant bloqués. Se déclenche uniquement pour une réponse par mot-clé : un désabonnement enregistré avec POST /v1/compliance/opt-outs ne le déclenche pas.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "opt_out.recorded",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "phone_number": "+1613XXXXXXX",
    "from_number": "+1416XXXXXXX",
    "keyword": "STOP"
  }
}
Champ de dataSignification
phone_numberL’abonné qui a texté le mot-clé, au format E.164 : la personne à qui cesser ou recommencer d’écrire.
from_numberVotre numéro HonkIO qui a reçu le mot-clé, au format E.164. Un désabonnement vaut pour les messages envoyés depuis ce numéro.
keywordLa réponse de l’abonné, rognée et en majuscules, par exemple STOP ou STOP PLEASE. La réponse entière, pas seulement le mot-clé : elle a été reconnue par son premier mot.

opt_out.reinstated

Un abonné désabonné a répondu START ou UNSTOP, vous pouvez donc de nouveau lui écrire depuis ce numéro. Se déclenche uniquement pour une réponse par mot-clé, et seulement si un désabonnement existait : un START de quelqu’un qui ne s’était jamais désabonné est un simple message.received.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "opt_out.reinstated",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "phone_number": "+1613XXXXXXX",
    "from_number": "+1416XXXXXXX",
    "keyword": "START"
  }
}
Champ de dataSignification
phone_numberL’abonné qui a texté le mot-clé, au format E.164 : la personne à qui cesser ou recommencer d’écrire.
from_numberVotre numéro HonkIO qui a reçu le mot-clé, au format E.164. Un désabonnement vaut pour les messages envoyés depuis ce numéro.
keywordLa réponse de l’abonné, rognée et en majuscules, par exemple START. La réponse entière, pas seulement le mot-clé : elle a été reconnue par son premier mot.

phone_number.suspended

Le loyer mensuel n’a pas pu être prélevé, le numéro a donc été suspendu. Rechargez votre solde pour le récupérer avant la fin du délai de grâce.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "phone_number.suspended",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "phone_number": "+1416XXXXXXX",
    "reason": "insufficient_balance",
    "monthly_cost_cents": 299
  }
}
Champ de dataSignification
phone_numberVotre numéro HonkIO, au format E.164.
reasonToujours insufficient_balance : le loyer du mois n’a pas pu être prélevé.
monthly_cost_centsLe loyer mensuel du numéro, en cents canadiens.

phone_number.released

Le numéro a quitté votre compte et ne peut pas être récupéré. Cessez d’y acheminer quoi que ce soit.

json
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "type": "phone_number.released",
  "created": "2026-09-24T12:00:00.000Z",
  "account_id": "clxxxaccountxxxxxxxxxxxxx",
  "livemode": true,
  "data": {
    "phone_number": "+1416XXXXXXX",
    "reason": "customer_released"
  }
}
Champ de dataSignification
phone_numberVotre numéro HonkIO, au format E.164.
reasonsuspended_grace_expired quand une suspension a dépassé son délai de grâce, customer_released quand vous l’avez libéré vous-même, admin_released quand le personnel de HonkIO l’a libéré.

Codes d'erreur

Les codes ci-dessous sont propres aux SMS, aux numéros de téléphone, à la vérification et à la LCAP. Tout appel peut aussi répondre avec les codes généraux du tableau d’erreurs de la plateforme, comme VALIDATION_ERROR, INSUFFICIENT_BALANCE et NOT_FOUND.

HTTPCodeSignification
403NUMBER_LIMIT_REACHEDLe compte a atteint sa limite de numéros de téléphone
403PHONE_NUMBER_NOT_OWNEDLe numéro from n’est pas provisionné sur ce compte.
403ALLOW_LIST_BLOCKEDLe destinataire ne figure pas sur la liste ALLOW de cette clé API
403DENY_LIST_BLOCKEDLe destinataire figure sur la liste DENY de cette clé API
403SEND_LIMIT_REQUEST_TOO_EARLYLes demandes de volume s’ouvrent une fois la période probatoire du compte terminée.
404VERIFICATION_NOT_FOUNDIdentifiant de vérification introuvable ou non associé à ce compte
404CONTACT_NOT_FOUNDContact introuvable
404CONTACT_GROUP_NOT_FOUNDGroupe de contacts introuvable
404CONTACT_GROUP_MEMBER_NOT_FOUNDCe contact n’est pas membre de ce groupe
404CONTACT_LIST_NOT_FOUNDListe de contacts introuvable
404CONTACT_LIST_ENTRY_NOT_FOUNDEntrée de liste introuvable
409VERIFICATION_ALREADY_VERIFIEDCe numéro a déjà été vérifié
409PURCHASE_IN_PROGRESSUn autre achat de numéro est en cours. Réessayez sous peu
409ALLOWANCE_REQUEST_PENDINGUne demande d'allocation est déjà en attente d'examen
409PHONE_NUMBER_SUSPENDEDLes frais mensuels de ce numéro n’ont pas pu être prélevés. Rechargez votre solde pour le réactiver.
409SEND_LIMIT_REQUEST_PENDINGUne demande de volume est déjà en attente d’examen.
409IDEMPOTENCY_KEY_REUSEDLa même clé Idempotency-Key a été envoyée avec un to, from ou body différent.
409CONTACT_ALREADY_EXISTSUn contact avec ce numéro de téléphone existe déjà
409CONTACT_GROUP_ALREADY_EXISTSUn groupe de contacts avec ce nom existe déjà
409CONTACT_GROUP_MEMBER_EXISTSLe contact est déjà membre de ce groupe
409CONTACT_LIST_ALREADY_EXISTSUne liste de contacts avec ce nom existe déjà
409CONTACT_LIST_ENTRY_EXISTSCette entrée existe déjà dans la liste
410VERIFICATION_EXPIREDLe code de vérification a expiré
422NON_CANADIAN_NUMBERNuméro E.164 canadien invalide
422UNDELIVERABLE_NUMBERLe numéro a échoué trois fois de suite chez l’opérateur (tous clients confondus) ; refusé sans frais pendant 90 jours.
422NOT_A_MOBILE_NUMBERLe numéro de destination est une ligne fixe ou un numéro VoIP. L’opérateur le refuse avant l’envoi ; rien n’est facturé.
422RESERVED_DESTINATIONLe numéro de destination appartient à un central réservé (555-XXXX, N11, codes de test des opérateurs). Refusé avant tout envoi ; rien n’est facturé.
422MESSAGE_TOO_LONGLe texte dépasserait la limite de 10 parties SMS de l'opérateur (≈1 530 caractères GSM-7 ou 670 caractères Unicode). Rien n'est facturé
422VERIFICATION_INVALID_CODECode incorrect. attempts_remaining indique le nombre de tentatives restantes
422INVALID_ALLOWANCE_REQUESTL'allocation demandée doit dépasser votre limite actuelle
422LINK_SHORTENER_BLOCKEDLes raccourcisseurs de liens sont refusés car les opérateurs les filtrent. Utilisez l’URL complète
422BROADCAST_TOO_LARGEGroupe de contacts trop grand pour une seule diffusion (250 membres par défaut). Divisez-le et envoyez par lots
422CANNOT_ERASE_OWN_NUMBERCe numéro appartient à votre compte. L’effacement vise uniquement le numéro d’un abonné
422TOO_MANY_AREA_CODESRecherchez au plus 25 indicatifs régionaux à la fois
422INVALID_PHONE_NUMBERNuméro de téléphone E.164 invalide
422CONTACT_LIST_ENTRY_INVALIDChaque entrée doit spécifier exactement l’un des champs phone_number, contact_id ou contact_group_id
429RECIPIENT_RATE_LIMITEDPlus de 30 messages vers un même destinataire en une heure ou 100 en une journée ; réessayez après l’en-tête Retry-After.
429VERIFICATION_MAX_ATTEMPTSTrop de tentatives incorrectes. Cette vérification est verrouillée
429DAILY_LIMIT_REACHEDLimite d’envoi quotidienne atteinte (les détails indiquent votre limite et le compte). Demandez un volume plus élevé une fois admissible
429FANOUT_LIMIT_REACHEDCe message identique a déjà atteint le nombre maximal de destinataires distincts autorisé sur 24 heures
429NUMBER_RATE_LIMITEDCe numéro d’envoi a atteint sa limite par minute. Réessayez sous peu
429BROADCAST_LIMIT_REACHEDLimite de diffusions atteinte pour ce compte au cours des 24 dernières heures
451OPT_OUT_BLOCKEDLe destinataire s’est désabonné. L’envoi est donc bloqué par la loi.
451NO_CONSENTPas de consentement LCAP valide en dossier
451CONSENT_EXPIREDConsentement tacite expiré (limite LCAP de 2 ans)
451DNCL_BLOCKEDNuméro sur la LNNTE du CRTC sans exemption applicable (à venir; non retourné actuellement)
501DNCL_COMING_SOONLa vérification de la liste nationale de numéros de télécommunication exclus du CRTC n’est pas encore disponible.
501TOLLFREE_800_COMING_SOONLes numéros 1-800 ne sont pas encore disponibles. Choisissez un autre préfixe sans frais ou un numéro local.
502CARRIER_ERRORL’opérateur a renvoyé une erreur lors de l’envoi.
502PROVISIONING_FAILEDL’opérateur n’a pas pu compléter cet achat de numéro. Rien n’a été facturé.
502NUMBER_SEARCH_FAILEDLa recherche de numéros est temporairement indisponible.
502RELEASE_FAILEDL’opérateur n’a pas accepté la libération. Le numéro vous appartient toujours ; réessayez sous peu
503CARRIER_UNAVAILABLEL’opérateur est injoignable ; rien n’a été envoyé ni facturé. Réessayez sous peu.
503CARRIER_TIMEOUTL’opérateur n’a pas répondu à temps : le message a peut-être été envoyé, ou non. Rien n’est facturé. Vérifiez le statut du message avant de l’envoyer de nouveau : une nouvelle tentative avec la même Idempotency-Key renvoie le message en échec au lieu de l’envoyer une seconde fois.