Documentation de l’API Bulk Template Message — v2
Cette API vous permet d’envoyer simultanément des messages modèles à plusieurs destinataires via le canal WhatsApp.
L’envoi est traité de manière asynchrone : la requête API renvoie immédiatement un bulkOperationId, tandis que les messages sont mis en file d’attente et traités en arrière-plan.
POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/send.js
Chaque endpoint de ce document est une custom function. Le corps de la réponse HTTP contient les champs propres à la fonction (_id, title, pathString…) et la réponse réelle de l’endpoint sous result. Les exemples de réponse de ce document montrent le contenu de result :
{
"_id": "function_id",
"title": "Show",
"pathString": "/bulk-message-api-app/api/v2/bulk-operation/show.js",
"result": {
"bulkOperation": { "_id": "abc123xyz789", "status": "running" }
}
}
Migrer depuis la v1
La v2 fonctionne en parallèle de la v1 : vous pouvez donc migrer à votre rythme. Les endpoints v1 (/api/bulk-message/…, /api/bulk-message-records/…) seront retirés ; les nouvelles intégrations doivent utiliser la v2.
- Passez les chemins des endpoints en v2 :
…/api/bulk-message/…devient…/api/v2/bulk-operation/…, et…/api/bulk-message-records/…devient…/api/v2/bulk-operation-items/…. Le corps de la requêtesendne change pas ; une seule requête accepte désormais jusqu’à 100 000 destinataires au lieu de 5 000. - Envoyez
bulkOperationIdau lieu debulkMessageIddans les requêtes. - Remplacez
stats.jsparshow.js. - Lisez les réponses avec les nouveaux noms de champs du tableau ci-dessous.
Les endpoints v2 ne connaissent que les opérations en masse créées avec le send v2. Pour suivre un message en masse créé avec la v1 avant votre migration, continuez à utiliser les endpoints v1 pour celui-ci.
Endpoints
| v1 | v2 | Ce qui change dans la réponse |
|---|---|---|
bulk-message/send.js | v2/bulk-operation/send.js | { bulkMessageId } devient { bulkOperationId } |
bulk-message/stats.js | v2/bulk-operation/show.js | { bulkMessage, stats } devient { bulkOperation } |
bulk-message/message-stats.js | v2/bulk-operation/message-stats.js | Les compteurs sont à la racine au lieu d’être sous messageStats |
bulk-message/stop.js | v2/bulk-operation/stop.js | Rien |
bulk-message-records/list.js | v2/bulk-operation-items/list.js | bulkMessageRecords devient bulkOperationItems |
bulk-message-records/list-with-messages.js | v2/bulk-operation-items/list-with-messages.js | Comme pour list, et chaque élément contient aussi session |
Noms des champs
| v1 | v2 |
|---|---|
stats.success | bulkOperation.progress.succeeded |
stats.pending, stats.failed, stats.cancelled, stats.total | bulkOperation.progress.pending, .failed, .cancelled, .total |
| — | bulkOperation.progress.unknown, bulkOperation.status, bulkOperation.result.outcome |
bulkMessage (message en masse) | bulkOperation (opération en masse) |
bulkMessageRecord (enregistrement) | bulkOperationItem (élément) |
bulkMessageId dans les requêtes, dans la réponse de send et sur les enregistrements | bulkOperationId |
username / userId / sessionId de l’enregistrement, tels qu’envoyés | payload.username / payload.userId / payload.sessionId |
referenceId de l’enregistrement | payload.referenceId |
payload de l’enregistrement (le payload de modèle du destinataire) | payload.payload |
userId, sessionId, messageId de l’enregistrement après l’envoi | result.userId, result.sessionId, result.messageId |
statut d’enregistrement success | succeeded |
errorMessage sur les enregistrements en échec | error.message, à côté de error.code et error.retryable |
| — | position (l’index du destinataire dans recipients) |
filtre {"status": "success"} | {"status": "succeeded"} |
filtre {"referenceId": "…"} | {"payload.referenceId": "…"} |
erreurs de send schema-error, invalidRequest | zod-schema-error, avec le chemin du champ invalide dans le message |
Principales fonctionnalités
- Envoyez des messages modèles en masse à 1 à 100 000 destinataires en une seule requête
- Variables personnalisées pour chaque destinataire (HEADER, BODY, BUTTONS, CAROUSEL)
- Un
defaultPayloadpartagé par tous les destinataires, avec fusion dupayloadpropre à chaque destinataire (isMerge) - Envoi programmé (
scheduleAt) - Indiquez les destinataires avec
username(numéro de téléphone),userIdousessionId - Envoyez plusieurs messages au même utilisateur dans une seule requête en masse
referenceIdfacultatif pour rapprocher les enregistrements de systèmes externes- Attribuez automatiquement les conversations à un utilisateur ou à un groupe de travail après l’envoi (
action)
Cette API ne peut envoyer que des modèles approuvés par Meta.
Le nom du modèle et le code de langue doivent correspondre exactement au modèle enregistré dans Meta Business Manager.
Paramètres de la requête
Paramètres de premier niveau
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
channelUsername | string | Oui | Numéro du canal WhatsApp utilisé pour envoyer le message (par ex. "908506669933") |
connectorBrand | string | Oui | Marque du connecteur de canal. Valeur actuellement prise en charge : "whatsapp" |
title | string | Non | Titre de la campagne. Utilisé pour l’affichage dans le tableau de bord. Par défaut : "-" |
scheduleAt | string (ISO 8601) | Non | Date et heure d’envoi programmé (par ex. "2026-06-10T10:00:00.000Z"). Si omis, les messages sont envoyés immédiatement. |
defaultPayload | object | Conditionnel* | Payload de modèle par défaut appliqué à tous les destinataires. Obligatoire si un destinataire ne fournit pas son propre payload. |
recipients | array | Oui | Liste des destinataires. Minimum 1, maximum 100 000 destinataires. |
action | object | Non | Action appliquée à la conversation après l’envoi. assign-to-user ou assign-to-workgroup. |
*Si chaque destinataire fournit son propre payload, defaultPayload est facultatif.
Structure d’un destinataire
Pour chaque destinataire, un seul des champs username, userId ou sessionId doit être fourni. Plusieurs identifiants ne peuvent pas être utilisés ensemble, et les omettre tous provoque une erreur.
Pour envoyer plusieurs messages au même utilisateur dans une seule requête en masse, ajoutez plusieurs fois le même identifiant à la liste des destinataires.
| Champ | Type | Description |
|---|---|---|
username | string | Numéro de téléphone du destinataire (par ex. "905554443322"). Si l’utilisateur n’existe pas dans le système, il est créé automatiquement. |
userId | string | Identifiant d’un utilisateur existant dans le système. |
sessionId | string | Identifiant d’une session de conversation existante dans le système. |
referenceId | string | Identifiant de référence facultatif (256 caractères maximum) pour le rapprochement avec les enregistrements de systèmes externes (CRM, commande, ticket, etc.). Doit être unique dans l’envoi en masse s’il est fourni. |
payload | object | Payload de modèle propre au destinataire (facultatif). Si isMerge: true, il est fusionné avec defaultPayload. |
isMerge | boolean | Remplace le comportement de fusion pour ce destinataire (facultatif). Si omis, defaultPayload.isMerge est utilisé. |
Structure de l’action
| Champ | Type | Description |
|---|---|---|
action.key | string | "assign-to-user" attribue la conversation à un agent précis ; "assign-to-workgroup" l’attribue à un groupe de travail. |
action.value | string | Identifiant de l’utilisateur ou du groupe de travail cible. |
Structure du payload de modèle
defaultPayload et le payload propre à un destinataire ont la même structure. Pour les messages modèles, type: "template" doit être indiqué.
{
"type": "template",
"template": {
"templateName": "order_confirmation",
"languageCode": "tr-TR",
"variables": []
},
"isMerge": true
}
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
type | string | Oui | Type de payload. Pour les messages modèles : "template" |
template.templateName | string | Oui* | Nom du modèle enregistré dans Meta. Facultatif dans les payloads de destinataires lorsque isMerge: true est utilisé (hérité du payload par défaut). |
template.languageCode | string | Non | Code de langue du modèle (par ex. "tr", "en", "tr-TR", "en-US"). Doit correspondre exactement à la langue enregistrée dans Meta. |
template.variables | array | Non | Liste des variables appliquées aux composants du modèle. Détaillée ci-dessous. |
isMerge | boolean | Non | true : le payload du destinataire est fusionné en profondeur avec defaultPayload. false ou omis : le payload du destinataire remplace entièrement defaultPayload. |
- Si
defaultPayload.isMerge: trueet que le destinataire fournit son proprepayload, une fusion en profondeur est appliquée. - Avec la fusion en profondeur, le destinataire peut ne fournir que
variables;templateNameetlanguageCodesont hérités du payload par défaut. - Le champ
isMergedu destinataire remplacedefaultPayload.isMerge. - Si un destinataire ne fournit pas de
payload,defaultPayloadest toujours utilisé.
Structure des variables
Chaque élément du tableau variables correspond à un composant du modèle. Un type de composant (type) ne peut apparaître qu’une seule fois dans le tableau.
| type | Description | parameters | cards |
|---|---|---|---|
"HEADER" | Composant d’en-tête du modèle. Variable de texte ou URL de média (image, vidéo, document) | Oui | — |
"BODY" | Variables de texte du corps du modèle | Oui | — |
"BUTTONS" | Variables de suffixe d’URL dynamique pour les boutons | Oui | — |
"CAROUSEL" | Composants de cartes pour les modèles carrousel | — | Oui |
Formats des paramètres
Le champ parameters peut être fourni sous trois formats différents :
// Format 1: Array (index-based) — for placeholders like {{1}}, {{2}}, {{3}}
"parameters": ["value1", "value2", "value3"]
// Format 2: Object (numeric keys) — alternative representation for {{1}}, {{2}}
"parameters": { "1": "value1", "2": "value2" }
// Format 3: Object (named keys) — for placeholders like {{name}}, {{amount}}
"parameters": { "name": "John", "amount": "150 USD" }
- Si votre modèle Meta utilise des espaces réservés comme
{{1}},{{2}}, utilisez le format tableau ou le format objet à clés numériques. - Si votre modèle utilise des espaces réservés nommés comme
{{firstName}}ou{{orderTotal}}, utilisez le format objet à clés nommées. - Avec les tableaux, l’ordre compte : le premier élément correspond à
{{1}}, le deuxième à{{2}}, et ainsi de suite.
Composant HEADER
Utilisez le composant HEADER lorsque l’en-tête du modèle contient un média (image, vidéo, document) ou du texte dynamique.
// Media header (image URL)
{
"type": "HEADER",
"parameters": ["https://example.com/product-image.jpg"]
}
// Text header variable
{
"type": "HEADER",
"parameters": ["Special Campaign"]
}
Composant BODY
Utilisé pour les variables de texte dynamiques dans le corps du modèle.
// Array format — when the template uses {{1}}, {{2}}, {{3}}
{
"type": "BODY",
"parameters": ["John", "ORD-12345", "150.00 USD"]
}
// Named format — when the template uses {{name}}, {{orderAmount}}
{
"type": "BODY",
"parameters": {
"name": "John",
"orderAmount": "150.00 USD"
}
}
Composant BUTTONS
Utilisé pour les boutons avec un suffixe d’URL dynamique. Si le modèle contient plusieurs boutons URL, seuls les boutons qui ont des variables sont indexés séquentiellement (les boutons sans variables ou les boutons FLOW sont ignorés).
// Single URL button suffix variable
{
"type": "BUTTONS",
"parameters": ["tracking/ORD-12345"]
}
// Multiple URL button suffix variables (first URL button, second URL button)
{
"type": "BUTTONS",
"parameters": ["tracking/ORD-12345", "cancel/ORD-12345"]
}
Composant CAROUSEL
Pour les modèles carrousel, les variables des composants sont définies séparément pour chaque carte.
{
"type": "CAROUSEL",
"cards": [
{
"components": [
{
"type": "HEADER",
"parameters": ["https://example.com/card1.jpg"]
},
{
"type": "BODY",
"parameters": ["Product A", "99.00 USD"]
}
]
},
{
"components": [
{
"type": "HEADER",
"parameters": ["https://example.com/card2.jpg"]
},
{
"type": "BODY",
"parameters": ["Product B", "149.00 USD"]
}
]
}
]
}
La longueur du tableau cards doit correspondre exactement au nombre de cartes définies dans le modèle.
Exemples
1. Modèle simple — sans variables, même contenu pour tous les destinataires
C’est la façon la plus simple d’envoyer à plusieurs destinataires un modèle statique sans variables.
{
"channelUsername": "908506669933",
"connectorBrand": "whatsapp",
"title": "General Announcement",
"defaultPayload": {
"type": "template",
"template": {
"templateName": "hello_world",
"languageCode": "tr-TR"
}
},
"recipients": [{ "username": "905554443301" }, { "username": "905554443302" }, { "username": "905554443303" }]
}
curl --location 'https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/send.js' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN' \
--data '{
"channelUsername": "908506669933",
"connectorBrand": "whatsapp",
"title": "General Announcement",
"defaultPayload": {
"type": "template",
"template": {
"templateName": "hello_world",
"languageCode": "tr-TR"
}
},
"recipients": [
{ "username": "905554443301" },
{ "username": "905554443302" },
{ "username": "905554443303" }
]
}'
- Les destinataires indiqués via
usernamesont créés automatiquement s’ils n’existent pas déjà dans le système - Tous les destinataires utilisent le même
defaultPayload; aucune donnée propre à un destinataire n’est nécessaire - Quand
isMergen’est pas indiqué, chaque destinataire utilise directement ledefaultPayload
2. Modèle avec variables de corps partagées
Le même modèle est envoyé à tous les destinataires avec des valeurs de variables identiques. Le modèle contient des espaces réservés tels que {{1}} et {{2}}.
{
"channelUsername": "908506669933",
"connectorBrand": "whatsapp",
"title": "Summer Campaign",
"defaultPayload": {
"type": "template",
"template": {
"templateName": "promo_notification",
"languageCode": "tr-TR",
"variables": [
{
"type": "BODY",
"parameters": ["30%", "July 31, 2026"]
}
]
}
},
"recipients": [{ "username": "905554443301" }, { "username": "905554443302" }]
}
- Dans cet exemple, le corps du modèle pourrait contenir un texte comme : « Profitez d’une remise de
{{1}}pendant notre campagne ! Offre valable jusqu’au{{2}}. » parameters[0]remplace{{1}}, tandis queparameters[1]remplace{{2}}
3. Modèle personnalisé — variables par destinataire avec isMerge
Des valeurs de variables différentes sont utilisées pour chaque destinataire. En définissant isMerge: true dans le defaultPayload, les destinataires n’ont qu’à fournir variables ; templateName et languageCode sont hérités du payload par défaut.
Dans cet exemple, le modèle comprend à la fois un en-tête média et des variables de corps personnalisées.
{
"channelUsername": "908506669933",
"connectorBrand": "whatsapp",
"title": "Personal Order Notification",
"defaultPayload": {
"type": "template",
"template": {
"templateName": "order_confirmation",
"languageCode": "tr-TR"
},
"isMerge": true
},
"recipients": [
{
"username": "905554443301",
"payload": {
"template": {
"variables": [
{
"type": "HEADER",
"parameters": ["https://s3.example.com/product-a.jpg"]
},
{
"type": "BODY",
"parameters": ["Ahmet", "ORD-001", "150.00 TRY"]
}
]
}
}
},
{
"username": "905554443302",
"payload": {
"template": {
"variables": [
{
"type": "HEADER",
"parameters": ["https://s3.example.com/product-b.jpg"]
},
{
"type": "BODY",
"parameters": ["Fatma", "ORD-002", "289.00 TRY"]
}
]
}
}
}
]
}
- Grâce à
isMerge: true,templateNamen’a pas besoin d’être répété dans chaque payload de destinataire - Le tableau
variablesde chaque destinataire est entièrement indépendant - L’URL du média d’en-tête doit être accessible publiquement
- Si
typeest omis dans le payload du destinataire,defaultPayload.typeest hérité
4. Variable de bouton URL
Utilisez le composant BUTTONS lorsque le modèle contient un bouton avec un suffixe d’URL dynamique. Par exemple, un numéro de suivi ajouté à l’URL de base https://example.com/track/.
{
"channelUsername": "908506669933",
"connectorBrand": "whatsapp",
"title": "Shipment Tracking Notification",
"defaultPayload": {
"type": "template",
"template": {
"templateName": "shipment_tracking",
"languageCode": "tr-TR"
},
"isMerge": true
},
"recipients": [
{
"username": "905554443301",
"payload": {
"template": {
"variables": [
{
"type": "BODY",
"parameters": ["Ahmet", "TK-98765"]
},
{
"type": "BUTTONS",
"parameters": ["TK-98765"]
}
]
}
}
},
{
"username": "905554443302",
"payload": {
"template": {
"variables": [
{
"type": "BODY",
"parameters": ["Recep", "TK-12345"]
},
{
"type": "BUTTONS",
"parameters": ["TK-12345"]
}
]
}
}
}
]
}
- Les paramètres
BUTTONSsont associés selon l’ordre des boutons qui contiennent des variables - Les boutons FLOW et les boutons sans variables sont ignorés ; l’indexation ne s’applique qu’aux boutons URL dynamiques
- Si l’URL du bouton du modèle est
https://example.com/track/{{1}}, alorsparameters[0]remplace{{1}}
5. Envoi programmé
Programmez l’envoi d’un message à une date et une heure futures. scheduleAt doit être fourni sous forme d’horodatage UTC ISO 8601.
{
"channelUsername": "908506669933",
"connectorBrand": "whatsapp",
"title": "Morning Campaign Message",
"scheduleAt": "2026-06-10T07:00:00.000Z",
"defaultPayload": {
"type": "template",
"template": {
"templateName": "morning_campaign",
"languageCode": "en-US"
},
"isMerge": true
},
"action": {
"key": "assign-to-workgroup",
"value": "WORKGROUP_ID"
},
"recipients": [
{
"username": "905554443301",
"payload": {
"template": {
"variables": [
{
"type": "BODY",
"parameters": { "name": "Ahmet", "discountRate": "25" }
}
]
}
}
},
{
"username": "905554443302",
"payload": {
"template": {
"variables": [
{
"type": "BODY",
"parameters": { "name": "Zeynep", "discountRate": "40" }
}
]
}
}
}
]
}
scheduleAtdoit être fourni dans le fuseau horaire UTC (par ex., pour 10 h 00 heure de Turquie, utilisez07:00:00.000Z)- Cet exemple utilise le format de paramètres nommés ; le modèle doit contenir des espaces réservés tels que
{{name}}et{{discountRate}} - Avec
action, les conversations créées par l’envoi du message sont automatiquement attribuées au groupe de travail indiqué
Réponse
Une requête réussie renvoie un bulkOperationId, qui peut servir à suivre le processus d’envoi :
{
"bulkOperationId": "abc123xyz789"
}
Réponses d’erreur
| Statut | Description |
|---|---|
recipients field is required | Le champ recipients n’a pas été envoyé |
Channel not found | Aucun canal vérifié correspondant à channelUsername et connectorBrand n’a été trouvé |
Channel is not verified | Le canal correspondant existe mais n’a pas encore été vérifié |
zod-schema-error | Un champ du corps de la requête a échoué à la validation du schéma (champ obligatoire manquant, type incorrect, valeur non prise en charge, plusieurs identifiants sur un destinataire ou payload manquant). Le message indique le champ, par ex. recipients.0.referenceId: referenceId must be unique |
Le bulkOperationId renvoyé peut être utilisé avec les endpoints ci-dessous pour consulter les statistiques d’envoi et arrêter le processus d’envoi.
Show — statut et statistiques de l’opération en masse
Récupère l’opération en masse avec son statut actuel et les compteurs d’envoi de ses éléments, sous la clé bulkOperation.
POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/show.js
Paramètres de la requête
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
bulkOperationId | string | Oui | Identifiant de l’opération en masse à suivre (renvoyé par send) |
{
"bulkOperationId": "abc123xyz789"
}
curl --location 'https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/show.js' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN' \
--data '{
"bulkOperationId": "abc123xyz789"
}'
Réponse
{
"bulkOperation": {
"_id": "abc123xyz789",
"type": "send-bulk-message",
"slug": "my-workspace",
"userId": "agent_user_id",
"payload": {
"title": "Campaign Title",
"channelUsername": "908506669933",
"connectorBrand": "whatsapp",
"connectorSlug": "om2WabVegaCloud",
"defaultLanguageCode": "tr-TR",
"sentUser": { "firstname": "John", "lastname": "Doe" },
"defaultPayload": {
"type": "template",
"template": { "templateName": "order_confirmation", "languageCode": "tr-TR" },
"isMerge": true
},
"source": { "type": "recipients", "bulkOperationInputGroupId": "group_id" }
},
"status": "running",
"result": { "outcome": "unknown", "hasErrors": false },
"progress": {
"total": 100,
"pending": 45,
"succeeded": 42,
"failed": 3,
"cancelled": 10,
"unknown": 0
},
"scheduleAt": "2026-06-10T07:00:00.000Z",
"createdAt": "2026-06-08T10:00:00.000Z",
"updatedAt": "2026-06-10T07:00:30.000Z",
"startedAt": "2026-06-10T07:00:05.000Z"
}
}
Champs de show
Les champs se trouvent sous bulkOperation.
| Champ | Type | Description |
|---|---|---|
status | string | Statut de l’opération en masse : preparing, scheduled, pending, running, stopping, stopped, completed, failed |
progress.total | number | Nombre total d’éléments |
progress.pending | number | Éléments encore en attente dans la file |
progress.succeeded | number | Éléments traités avec succès pour l’envoi |
progress.failed | number | Éléments ayant rencontré une erreur d’envoi |
progress.cancelled | number | Éléments annulés par stop |
progress.unknown | number | Éléments dont le résultat n’a pas pu être déterminé |
result.outcome | string | Résultat final une fois l’opération en masse terminée : success, partial, all-failed, cancelled, unknown |
result.hasErrors | boolean | true si au moins un élément a échoué |
scheduleAt | string | Heure d’envoi programmée (uniquement pour les opérations en masse programmées) |
startedAt, completedAt | string | Début et fin de l’envoi |
progress.succeeded indique que le message a été transmis avec succès par le système. Cela ne garantit pas que Meta a remis le message au destinataire. Les mises à jour de statut ultérieures générées par Meta (comme delivered, read ou failed) n’affectent pas ce compteur et n’y sont pas reflétées.
- Endpoint show : affiche le statut d’envoi des éléments (
pending,succeeded,failed,cancelled,unknown) - Endpoint message-stats : affiche l’état réel des messages (
pending,sending,sent,delivered,read,error, etc.) - Un élément peut être
succeededsans que le message soit encore remis ou lu
Identifiant de référence
Le referenceId est un champ facultatif que vous pouvez utiliser pour rapprocher les éléments d’envoi d’un identifiant de votre propre système à des fins de suivi (par ex. numéro de commande, identifiant de ticket). Il est renvoyé sur chaque élément sous la forme payload.referenceId.
S’il est fourni, il doit être unique dans l’envoi en masse (256 caractères maximum).
- Non obligatoire
- Ne peut pas être dupliqué dans une même opération en masse
- Peut être réutilisé dans différentes opérations en masse
Message Stats — statut des messages WhatsApp
Récupère les statistiques de statut des messages d’une opération en masse. Contrairement à l’endpoint show, il affiche le message.status réel des messages envoyés au lieu du statut des éléments.
Statuts de message possibles : pending, sending, sent, delivered, read, deleted, warning, error, unknown, not-sended
POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/message-stats.js
Paramètres de la requête
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
bulkOperationId | string | Oui | Identifiant de l’opération en masse à interroger |
{
"bulkOperationId": "abc123xyz789"
}
curl --location 'https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/message-stats.js' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN' \
--data '{
"bulkOperationId": "abc123xyz789"
}'
Réponse
{
"pending": 5,
"sending": 2,
"sent": 10,
"delivered": 45,
"read": 30,
"deleted": 2,
"warning": 1,
"error": 3,
"unknown": 0,
"not-sended": 2,
"no-message": 20,
"total": 120
}
Champs de Message Stats
| Champ | Type | Description |
|---|---|---|
pending | number | Messages pas encore envoyés |
sending | number | Messages en cours d’envoi |
sent | number | Messages transmis à WhatsApp, pas encore remis |
delivered | number | Messages remis sur le téléphone du destinataire |
read | number | Messages lus par le destinataire |
deleted | number | Messages supprimés |
warning | number | Messages avec avertissements |
error | number | Messages en erreur |
unknown | number | Messages de statut inconnu |
not-sended | number | Messages qui n’ont pas pu être envoyés |
no-message | number | Éléments sans message (encore en file, en échec ou annulés) |
total | number | Nombre total d’éléments |
- Un nombre élevé de
no-messagealors que l’opération en masse estrunningindique que l’envoi est encore en cours - Le nombre de
deliveredindique les messages remis avec succès - Le nombre de
readpeut servir à mesurer l’engagement de la campagne - Vous pouvez interroger cet endpoint périodiquement pour un suivi en temps réel
Structure des options — pagination, filtrage et tri
Le paramètre options utilisé dans les endpoints list et list-with-messages fournit une structure standard pour la pagination, le filtrage et le tri.
Champs des options
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
pagination | object | Non | Paramètres de pagination |
filtering | object | Non | Critères de filtrage |
sorting | object | Non | Paramètres de tri (par ex. {"position": 1}) |
Structure de la pagination
Si pagination n’est pas envoyé, la première page est renvoyée avec jusqu’à 50 éléments. S’il est envoyé, currentPage est obligatoire.
| Champ | Type | Par défaut | Description |
|---|---|---|---|
currentPage | number | — | Numéro de la page à récupérer (obligatoire lorsque pagination est envoyé) |
pageItems | number | 50 | Nombre d’éléments par page (maximum : 50) |
Structure du filtrage
Le filtrage utilise le format de requête MongoDB sur les champs des éléments, avec les noms de champs exactement tels qu’ils apparaissent dans la réponse (par ex. status, payload.referenceId, result.messageId). Dans l’endpoint list-with-messages, vous pouvez aussi filtrer sur les champs des messages (par ex. message.status).
// Filter by item status
{
"options": {
"filtering": {
"status": "succeeded"
}
}
}
// Filter by referenceId
{
"options": {
"filtering": {
"payload.referenceId": "ORDER-12345"
}
}
}
// Filter by message status (only list-with-messages)
{
"options": {
"filtering": {
"message.status": "delivered"
}
}
}
// Filter by multiple statuses using $in (MongoDB operator)
{
"options": {
"filtering": {
"message.status": {
"$in": ["delivered", "read"]
}
}
}
}
// Exclude specific statuses using $nin (MongoDB operator)
{
"options": {
"filtering": {
"message.status": {
"$nin": ["error", "not-sended"]
}
}
}
}
// List items that have no message (only list-with-messages)
{
"options": {
"filtering": {
"message": {
"$exists": false
}
}
}
}
Structure du tri
Le tri utilise le format de tri MongoDB. 1 signifie croissant, -1 décroissant. position est l’ordre du destinataire dans le tableau recipients de la requête (à partir de 0).
// Sort in the order the recipients were sent in the request
{
"options": {
"sorting": {
"position": 1
}
}
}
// Sort by multiple fields
{
"options": {
"sorting": {
"status": 1,
"position": 1
}
}
}
Exemple d’utilisation combinée
{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 2,
"pageItems": 50
},
"filtering": {
"message.status": "delivered"
},
"sorting": {
"position": 1
}
}
}
Cet exemple liste la deuxième page des messages remis, dans l’ordre des destinataires.
- Combinez
filteringetpaginationpour des requêtes ciblées - La réponse inclut
options.pagination.totalCountetoptions.pagination.totalPagespour la navigation - Le filtre
message.statusne fonctionne que dans l’endpointlist-with-messages
Items — détails et liste des éléments
Liste tous les éléments d’envoi d’une opération en masse. Chaque élément correspond à une entrée du tableau recipients.
List — liste des éléments
POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation-items/list.js
Paramètres de la requête
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
bulkOperationId | string | Oui | Identifiant de l’opération en masse à lister |
options | object | Non | Options de pagination, de filtrage et de tri. Voir Structure des options pour plus de détails. |
Requête — utilisation de base
{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
}
}
}
Requête — filtrer par statut précis
{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
},
"filtering": {
"status": "failed"
}
}
}
Liste uniquement les éléments d’envoi en échec, avec la raison dans error.
Réponse
{
"bulkOperationItems": [
{
"_id": "item_1",
"bulkOperationId": "abc123xyz789",
"bulkOperationBatchId": "batch_1",
"slug": "my-workspace",
"position": 0,
"payload": {
"username": "905554443301",
"referenceId": "ORDER-12345",
"payload": {
"template": {
"variables": [{ "type": "BODY", "parameters": ["Ahmet", "ORD-001"] }]
}
}
},
"status": "succeeded",
"attempt": 1,
"result": {
"userId": "user_123",
"sessionId": "session_456",
"messageId": "msg_789"
},
"createdAt": "2026-06-10T10:00:00.000Z",
"updatedAt": "2026-06-10T10:00:01.000Z",
"completedAt": "2026-06-10T10:00:01.000Z"
},
{
"_id": "item_2",
"bulkOperationId": "abc123xyz789",
"bulkOperationBatchId": "batch_1",
"slug": "my-workspace",
"position": 1,
"payload": { "username": "905554443302", "referenceId": "ORDER-12346" },
"status": "failed",
"attempt": 1,
"error": { "code": "error", "message": "Variable missing", "retryable": false },
"result": { "userId": "user_124", "sessionId": "session_457" },
"createdAt": "2026-06-10T10:00:00.000Z",
"updatedAt": "2026-06-10T10:00:01.000Z",
"completedAt": "2026-06-10T10:00:01.000Z"
}
],
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50,
"totalCount": 150,
"totalPages": 3
},
"sorting": {},
"filtering": {},
"searching": {}
}
}
List With Messages — liste avec les détails des messages
Offre les mêmes fonctionnalités que l’endpoint list (pagination, filtrage, tri). La différence : chaque élément contient aussi les objets message et session associés, lorsqu’ils existent. Cela vous permet de voir le statut du message (delivered, read, error, etc.) et son payload en une seule requête.
POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation-items/list-with-messages.js
Requête — utilisation de base
{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
}
}
}
Requête — filtrer par statut de message
{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
},
"filtering": {
"message.status": "delivered"
}
}
}
Cet exemple ne récupère que les messages au statut « delivered ».
Requête — lister les messages en échec
{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
},
"filtering": {
"message.status": "error"
}
}
}
Sert à examiner les messages qui ont rencontré des erreurs d’envoi.
Réponse
{
"bulkOperationItems": [
{
"_id": "item_1",
"bulkOperationId": "abc123xyz789",
"position": 0,
"payload": { "username": "905554443301", "referenceId": "ORDER-12345" },
"status": "succeeded",
"result": {
"userId": "user_123",
"sessionId": "session_456",
"messageId": "msg_789"
},
"message": {
"_id": "msg_789",
"type": "template",
"status": "delivered",
"payload": {
"template": {
"name": "order_notification"
}
},
"createdAt": "2026-06-10T10:01:00.000Z"
},
"session": {
"_id": "session_456",
"status": "opened"
}
}
],
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50,
"totalCount": 150,
"totalPages": 3
},
"sorting": {},
"filtering": {},
"searching": {}
}
}
Champs d’un élément
| Champ | Type | Description |
|---|---|---|
_id | string | Identifiant de l’élément |
bulkOperationId | string | Identifiant de l’opération en masse à laquelle appartient l’élément |
position | number | Ordre du destinataire dans le tableau recipients de la requête, à partir de 0 |
payload | object | Le destinataire exactement tel qu’envoyé dans la requête (username / userId / sessionId, referenceId, payload, isMerge) |
status | string | Statut de l’élément : pending, succeeded, failed, cancelled, unknown |
error | object | Uniquement sur les éléments failed : code, message et retryable |
result.userId | string | Identifiant de l’utilisateur destinataire |
result.sessionId | string | Identifiant de la session de conversation |
result.messageId | string | Identifiant du message envoyé (absent si aucun message n’a été envoyé) |
completedAt | string | Date de traitement de l’élément |
message | object | Détails du message (uniquement dans list-with-messages) |
session | object | Détails de la conversation (uniquement dans list-with-messages) |
Comparaison list et list-with-messages
| Fonctionnalité | list | list-with-messages |
|---|---|---|
| Pagination | Oui | Oui |
| Filtrage | Oui (champs d’élément uniquement) | Oui (champs d’élément et de message) |
| Message / Session | Non | Oui (s’ils existent) |
| Performance | Rapide | Moyenne (effectue une jointure) |
| Filtre message.status | Non disponible | Disponible |
- list : lister rapidement tous les éléments, filtrer par referenceId, vérifier les statuts des éléments
- list-with-messages : consulter les statuts des messages WhatsApp (delivered, read), inspecter les payloads des messages
- Exemples de filtrage :
{"status": "failed"}— éléments d’envoi en échec{"payload.referenceId": "ORDER-123"}— un identifiant de référence précis{"message.status": "delivered"}— messages remis (list-with-messages uniquement){"message.status": "read"}— messages lus (list-with-messages uniquement){"message.status": "error"}— messages en erreur (list-with-messages uniquement){"message.status": {"$in": ["delivered", "read"]}}— messages remis ou lus (list-with-messages uniquement){"message.status": {"$nin": ["error", "not-sended"]}}— messages hors erreurs et non-envoyés (list-with-messages uniquement){"message": {"$exists": false}}— éléments sans message (list-with-messages uniquement){"status": {"$in": ["succeeded", "pending"]}}— éléments réussis ou en attente
- L’endpoint
listest plus rapide ; privilégiez-le pour les vérifications générales de statut - Utilisez
list-with-messageslorsque les détails des messages sont nécessaires - Le nombre maximal d’éléments par page (pageItems) est de 50 ; la valeur par défaut est 50 si elle n’est pas indiquée
- Le filtre
message.statusne fonctionne que danslist-with-messages - Les éléments sans
result.messageIdn’ont pas d’objetmessage
Stop — annuler l’envoi
Arrête l’envoi en cours d’une opération en masse. Tous les éléments pending passent au statut cancelled et l’opération en masse passe à stopped. Les éléments déjà traités en succeeded ou failed ne sont pas concernés. Une opération en masse programmée peut aussi être arrêtée avant son heure scheduleAt.
POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/stop.js
Cette action est irréversible. Un envoi arrêté ne peut pas être repris.
Paramètres de la requête
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
bulkOperationId | string | Oui | Identifiant de l’opération en masse à arrêter |
{
"bulkOperationId": "abc123xyz789"
}
curl --location 'https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/stop.js' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN' \
--data '{
"bulkOperationId": "abc123xyz789"
}'
Réponse
Pour une requête réussie, le corps de la réponse est vide. Un statut HTTP 200 indique que l’opération s’est terminée avec succès.
{}
- Seules les opérations en masse créées via cette API peuvent être arrêtées
- Après l’arrêt d’un envoi, vous pouvez utiliser l’endpoint
showpour vérifier le nombre d’éléments annulés (bulkOperation.progress.cancelled) - Si l’opération en masse est introuvable, l’API renvoie l’erreur :
Bulk operation not found.