Guides

Envoyer des messages

Textes, médias, localisation et templates : formats, fenêtre de 24 heures, statuts, historique et erreurs.

Mis à jour le 12 novembre 2026Voir en Markdown

POST /messages accepte un message et répond tout de suite (202, statut queued). L'envoi réel à Meta, ses reprises éventuelles et le suivi des statuts se font en arrière-plan.

Les champs communs#

ChampDescription
phoneNumberIdLe numéro d'envoi : l'id renvoyé par la connexion du compte.
toLe destinataire, en format international avec « + » : +237600000000. Les espaces, tirets et parenthèses sont ignorés.
typetext, template, image, video, audio, document ou location.

Un template (à tout moment)#

Un template approuvé par Meta peut être envoyé à tout moment, même sans échange préalable. C'est la seule façon de démarrer une conversation. Voir Templates.

bash
curl -X POST "$INBOX_API/messages" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: rdv-2026-11-14-client-42" \
  -d '{
    "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
    "to": "+237600000000",
    "type": "template",
    "template": {
      "name": "rappel_rdv",
      "language": "fr",
      "components": [
        {
          "type": "body",
          "parameters": [
            {
              "type": "text",
              "text": "Awa"
            },
            {
              "type": "text",
              "text": "14 novembre à 10 h"
            }
          ]
        }
      ]
    }
  }'
Réponse · 202 Accepted
{
  "id": "d5ad1e3e-6ab9-4f7f-8e4e-b17104a7bd00",
  "direction": "outbound",
  "status": "queued",
  "type": "template",
  "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
  "to": "237600000000",
  "waMessageId": null,
  "content": {
    "type": "template",
    "template": {
      "name": "rappel_rdv",
      "language": "fr",
      "components": [
        {
          "type": "body",
          "parameters": [
            {
              "type": "text",
              "text": "Awa"
            },
            {
              "type": "text",
              "text": "14 novembre à 10 h"
            }
          ]
        }
      ]
    }
  },
  "error": null,
  "createdAt": "2026-11-12T08:33:45.855Z",
  "sentAt": null,
  "deliveredAt": null,
  "readAt": null
}

Un message libre : la fenêtre de 24 heures#

Un message libre n'est accepté que si le destinataire vous a écrit depuis moins de 24 heures sur ce numéro. Sinon :

Erreur réelle · HTTP 422
{
  "error": {
    "code": "outside_service_window",
    "message": "Ce correspondant ne vous a pas écrit depuis 24 h : seul un template approuvé peut lui être envoyé",
    "requestId": "req_4fdba006-98d4-4164-893d-98389fb54c62"
  }
}

Dans la fenêtre, tous les types de messages sont possibles.

Texte#

bash
curl -X POST "$INBOX_API/messages" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
    "to": "+237611111111",
    "type": "text",
    "text": {
      "body": "Bonjour Awa, nous avons bien reçu votre demande."
    }
  }'
Réponse · 202 Accepted
{
  "id": "2dbd95d9-4091-4cbf-8f75-c2dea9f39372",
  "direction": "outbound",
  "status": "queued",
  "type": "text",
  "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
  "to": "237611111111",
  "waMessageId": null,
  "content": {
    "type": "text",
    "text": {
      "body": "Bonjour Awa, nous avons bien reçu votre demande.",
      "previewUrl": false
    }
  },
  "error": null,
  "createdAt": "2026-11-12T08:33:46.048Z",
  "sentAt": null,
  "deliveredAt": null,
  "readAt": null
}

Le corps du texte fait de 1 à 4096 caractères.

Image#

bash
curl -X POST "$INBOX_API/messages" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
    "to": "+237611111111",
    "type": "image",
    "image": {
      "link": "https://exemple.com/plan-d-acces.jpg",
      "caption": "Plan d'\''accès au cabinet"
    }
  }'
Réponse · 202 Accepted
{
  "id": "097f6e1e-64f0-4bdf-8a00-672f6d7b168c",
  "direction": "outbound",
  "status": "queued",
  "type": "image",
  "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
  "to": "237611111111",
  "waMessageId": null,
  "content": {
    "type": "image",
    "media": {
      "link": "https://exemple.com/plan-d-acces.jpg",
      "caption": "Plan d'accès au cabinet"
    }
  },
  "error": null,
  "createdAt": "2026-11-12T08:33:46.070Z",
  "sentAt": null,
  "deliveredAt": null,
  "readAt": null
}

Pour les médias (image, video, audio, document), indiquez soit link (une URL https:// publique), soit id (un média déjà hébergé par Meta), jamais les deux. La caption n'existe pas pour l'audio. Le téléversement et le téléchargement de médias par l'API ne sont pas encore disponibles.

Document#

bash
curl -X POST "$INBOX_API/messages" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
    "to": "+237611111111",
    "type": "document",
    "document": {
      "link": "https://exemple.com/ordonnance.pdf",
      "filename": "ordonnance.pdf",
      "caption": "Votre ordonnance"
    }
  }'
Réponse · 202 Accepted
{
  "id": "9e09a76b-537b-4d5f-8454-4305ba8748a6",
  "direction": "outbound",
  "status": "queued",
  "type": "document",
  "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
  "to": "237611111111",
  "waMessageId": null,
  "content": {
    "type": "document",
    "media": {
      "link": "https://exemple.com/ordonnance.pdf",
      "caption": "Votre ordonnance",
      "filename": "ordonnance.pdf"
    }
  },
  "error": null,
  "createdAt": "2026-11-12T08:33:46.098Z",
  "sentAt": null,
  "deliveredAt": null,
  "readAt": null
}

Localisation#

bash
curl -X POST "$INBOX_API/messages" \
  -H "Authorization: Bearer $INBOX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
    "to": "+237611111111",
    "type": "location",
    "location": {
      "latitude": 4.0511,
      "longitude": 9.7679,
      "name": "Cabinet Dentaire Zokem",
      "address": "Akwa, Douala"
    }
  }'
Réponse · 202 Accepted
{
  "id": "a9691d7a-ee51-4da9-87b2-24cce79a9313",
  "direction": "outbound",
  "status": "queued",
  "type": "location",
  "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
  "to": "237611111111",
  "waMessageId": null,
  "content": {
    "type": "location",
    "location": {
      "latitude": 4.0511,
      "longitude": 9.7679,
      "name": "Cabinet Dentaire Zokem",
      "address": "Akwa, Douala"
    }
  },
  "error": null,
  "createdAt": "2026-11-12T08:33:46.131Z",
  "sentAt": null,
  "deliveredAt": null,
  "readAt": null
}

Suivre un message#

bash
curl "$INBOX_API/messages/ID" \
  -H "Authorization: Bearer $INBOX_KEY"
Réponse · 200 OK
{
  "id": "d5ad1e3e-6ab9-4f7f-8e4e-b17104a7bd00",
  "direction": "outbound",
  "status": "sent",
  "type": "template",
  "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
  "to": "237600000000",
  "waMessageId": "wamid.HBgMXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "content": {
    "type": "template",
    "template": {
      "name": "rappel_rdv",
      "language": "fr",
      "components": [
        {
          "type": "body",
          "parameters": [
            {
              "text": "Awa",
              "type": "text"
            },
            {
              "text": "14 novembre à 10 h",
              "type": "text"
            }
          ]
        }
      ]
    }
  },
  "error": null,
  "createdAt": "2026-11-12T08:33:45.855Z",
  "sentAt": "2026-11-12T08:33:45.891Z",
  "deliveredAt": null,
  "readAt": null
}

error vaut null tant que tout va bien ; en cas d'échec, il contient le code et le message de Meta. Pour être notifié sans interroger l'API, utilisez les webhooks (message.sent, message.delivered, message.read, message.failed).

L'historique#

bash
curl "$INBOX_API/messages" \
  -H "Authorization: Bearer $INBOX_KEY"
Réponse · 200 OK
{
  "data": [
    {
      "id": "a9691d7a-ee51-4da9-87b2-24cce79a9313",
      "direction": "outbound",
      "status": "sent",
      "type": "location",
      "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
      "to": "237611111111",
      "waMessageId": "wamid.HBgMXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
      "content": {
        "type": "location",
        "location": {
          "name": "Cabinet Dentaire Zokem",
          "address": "Akwa, Douala",
          "latitude": 4.0511,
          "longitude": 9.7679
        }
      },
      "error": null,
      "createdAt": "2026-11-12T08:33:46.131Z",
      "sentAt": "2026-11-12T08:33:46.251Z",
      "deliveredAt": null,
      "readAt": null
    },
    {
      "id": "9e09a76b-537b-4d5f-8454-4305ba8748a6",
      "direction": "outbound",
      "status": "sent",
      "type": "document",
      "phoneNumberId": "4d8e654e-d744-4bf3-8211-9ceffcccfe87",
      "to": "237611111111",
      "waMessageId": "wamid.HBgMXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
      "content": {
        "type": "document",
        "media": {
          "link": "https://exemple.com/ordonnance.pdf",
          "caption": "Votre ordonnance",
          "filename": "ordonnance.pdf"
        }
      },
      "error": null,
      "createdAt": "2026-11-12T08:33:46.098Z",
      "sentAt": "2026-11-12T08:33:46.221Z",
      "deliveredAt": null,
      "readAt": null
    }
  ],
  "nextCursor": "MjAyNi0xMS0xMlQwODozMzo0Ni4wOThafGMwYmU4NDdlLWQyNDQtNGUyOS04N2M1LTAyNTE3ODk1Yzg5OQ"
}

L'historique est trié du plus récent au plus ancien et paginé par curseur : tant que nextCursor n'est pas null, passez-le dans ?cursor= pour obtenir la page suivante. Filtres : phoneNumberId, direction (inbound, outbound), status, limit (1 à 100).

Les erreurs d'envoi#

CodeHTTPCause
invalid_message422Numéro mal formé, texte vide, média ambigu (id et link à la fois), lien non https
outside_service_window422Message libre hors de la fenêtre de 24 heures
template_not_approved422Le template existe mais n'est pas (ou plus) approuvé
idempotency_conflict422Même Idempotency-Key pour une requête différente
quota_exceeded402Quota mensuel de votre plan atteint (production)
not_found404Numéro inconnu, ou d'un autre environnement
Erreur réelle · HTTP 422
{
  "error": {
    "code": "invalid_message",
    "message": "Numéro destinataire invalide : utilisez le format international avec « + », ex. +237600000000",
    "requestId": "req_72871759-6841-4dcf-8065-3425c4a44633"
  }
}

Les refus de Meta pendant l'envoi (template introuvable, destinataire non joignable…) ne se voient pas dans la réponse 202 : le message passe en failed, avec le motif de Meta, et vous recevez message.failed.