Skip to main content

Bulk Template Message API Documentation — v2

This API allows you to send template messages to multiple recipients simultaneously through the WhatsApp channel.

Message delivery is processed asynchronously; the API request immediately returns a bulkOperationId, while messages are queued and processed in the background.

POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/send.js
Response format

Every endpoint in this document is a custom function. The HTTP response body carries the function's own fields (_id, title, pathString…) and the endpoint's actual response under result. The response examples in this document show the content of result:

{
"_id": "function_id",
"title": "Show",
"pathString": "/bulk-message-api-app/api/v2/bulk-operation/show.js",
"result": {
"bulkOperation": { "_id": "abc123xyz789", "status": "running" }
}
}

Migrating from v1​

v2 runs side by side with v1, so you can migrate on your own schedule. The v1 endpoints (/api/bulk-message/…, /api/bulk-message-records/…) will be retired; new integrations should use v2.

  1. Move the endpoint paths to v2: …/api/bulk-message/… becomes …/api/v2/bulk-operation/…, and …/api/bulk-message-records/… becomes …/api/v2/bulk-operation-items/…. The send request body does not change; a single request now takes up to 100,000 recipients instead of 5,000.
  2. Send bulkOperationId instead of bulkMessageId in requests.
  3. Replace stats.js with show.js.
  4. Read the responses with the new field names in the table below.

The v2 endpoints only know bulk operations created with the v2 send. To track a bulk message created with v1 before you switched, keep using the v1 endpoints for it.

Endpoints​

v1v2What changes in the response
bulk-message/send.jsv2/bulk-operation/send.js{ bulkMessageId } becomes { bulkOperationId }
bulk-message/stats.jsv2/bulk-operation/show.js{ bulkMessage, stats } becomes { bulkOperation }
bulk-message/message-stats.jsv2/bulk-operation/message-stats.jsThe counters are at the root instead of under messageStats
bulk-message/stop.jsv2/bulk-operation/stop.jsNothing
bulk-message-records/list.jsv2/bulk-operation-items/list.jsbulkMessageRecords becomes bulkOperationItems
bulk-message-records/list-with-messages.jsv2/bulk-operation-items/list-with-messages.jsSame as list, and each item also carries session

Field names​

v1v2
stats.successbulkOperation.progress.succeeded
stats.pending, stats.failed, stats.cancelled, stats.totalbulkOperation.progress.pending, .failed, .cancelled, .total
—bulkOperation.progress.unknown, bulkOperation.status, bulkOperation.result.outcome
bulkMessage (bulk message)bulkOperation (bulk operation)
bulkMessageRecord (record)bulkOperationItem (item)
bulkMessageId in requests, in the send response and on recordsbulkOperationId
record username / userId / sessionId as sentpayload.username / payload.userId / payload.sessionId
record referenceIdpayload.referenceId
record payload (the recipient's template payload)payload.payload
record userId, sessionId, messageId after sendingresult.userId, result.sessionId, result.messageId
record status successsucceeded
record errorMessage on failed recordserror.message, next to error.code and error.retryable
—position (the recipient's index in recipients)
filter {"status": "success"}{"status": "succeeded"}
filter {"referenceId": "…"}{"payload.referenceId": "…"}
send errors schema-error, invalidRequestzod-schema-error, with the path of the invalid field in the message

Key Features​

  • Send bulk template messages to 1–100,000 recipients in a single request
  • Personalized variables for each recipient (HEADER, BODY, BUTTONS, CAROUSEL)
  • Shared defaultPayload for all recipients with recipient-specific payload merging (isMerge)
  • Scheduled delivery (scheduleAt)
  • Specify recipients using username (phone number), userId, or sessionId
  • Send multiple messages to the same user within a single bulk request
  • Optional referenceId for tracking records with external systems
  • Automatically assign conversations to a user or workgroup after sending (action)
warning

This API can only send templates that have been approved by Meta.

The template name and language code must exactly match the template registered in Meta Business Manager.

Request Parameters​

Top-Level Parameters​

ParameterTypeRequiredDescription
channelUsernamestringYesWhatsApp channel number used to send the message (e.g. "908506669933")
connectorBrandstringYesChannel connector brand. Currently supported value: "whatsapp"
titlestringNoCampaign title. Used for display in the dashboard. Default: "-"
scheduleAtstring (ISO 8601)NoScheduled delivery date and time (e.g. "2026-06-10T10:00:00.000Z"). If omitted, messages are sent immediately.
defaultPayloadobjectConditional*Default template payload applied to all recipients. Required if a recipient does not provide its own payload.
recipientsarrayYesRecipient list. Minimum 1, maximum 100,000 recipients.
actionobjectNoAction applied to the conversation after sending. assign-to-user or assign-to-workgroup.

*If every recipient provides its own payload, defaultPayload is optional.

Recipient Structure​

For each recipient, exactly one of username, userId, or sessionId must be provided. Multiple identifiers cannot be used together, and omitting all of them will result in an error.

To send multiple messages to the same user within a single bulk request, add the same identifier to the recipient list multiple times.

FieldTypeDescription
usernamestringRecipient's phone number (e.g. "905554443322"). If the user does not exist in the system, it is created automatically.
userIdstringID of an existing user in the system.
sessionIdstringID of an existing conversation session in the system.
referenceIdstringOptional reference ID (max 256 characters) for matching with external system records (CRM, order, ticket, etc.). Must be unique within the bulk if provided.
payloadobjectRecipient-specific template payload (optional). If isMerge: true, it is merged with defaultPayload.
isMergebooleanOverrides merge behavior for this recipient (optional). If omitted, defaultPayload.isMerge is used.

Action Structure​

FieldTypeDescription
action.keystring"assign-to-user" assigns the conversation to a specific agent; "assign-to-workgroup" assigns it to a workgroup.
action.valuestringID of the target user or workgroup.

Template Payload Structure​

defaultPayload and recipient-specific payload share the same structure. For template messages, type: "template" must be specified.

{
"type": "template",
"template": {
"templateName": "order_confirmation",
"languageCode": "tr-TR",
"variables": []
},
"isMerge": true
}
FieldTypeRequiredDescription
typestringYesPayload type. For template messages: "template"
template.templateNamestringYes*Template name registered in Meta. Optional in recipient payloads when isMerge: true is used (inherits from default).
template.languageCodestringNoTemplate language code (e.g. "tr", "en", "tr-TR", "en-US"). Must exactly match the language registered in Meta.
template.variablesarrayNoList of variables applied to template components. Detailed below.
isMergebooleanNotrue: recipient payload is deep-merged with defaultPayload. false or omitted: recipient payload completely replaces defaultPayload.
isMerge Behavior
  • If defaultPayload.isMerge: true and the recipient provides its own payload, a deep merge is applied.
  • With deep merge, the recipient may provide only variables; templateName and languageCode are inherited from the default payload.
  • The recipient's isMerge field overrides defaultPayload.isMerge.
  • If a recipient does not provide a payload, defaultPayload is always used.

Variables Structure​

Each item in the variables array corresponds to a template component. A component type (type) may appear only once in the array.

typeDescriptionparameterscards
"HEADER"Template header component. Text variable or media URL (image, video, document)Yes—
"BODY"Template body text variablesYes—
"BUTTONS"Dynamic URL suffix variables for buttonsYes—
"CAROUSEL"Card components for carousel templates—Yes

Parameter Formats​

The parameters field can be provided in three different formats:

// 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" }
Which format should I use?
  • If your Meta template uses placeholders like {{1}}, {{2}}, use either the array format or numeric-key object format.
  • If your template uses named placeholders such as {{firstName}} or {{orderTotal}}, use the named-key object format.
  • When using arrays, order matters: the first element maps to {{1}}, the second to {{2}}, and so on.

HEADER Component​

Use the HEADER component when the template header contains media (image, video, document) or dynamic text.

// Media header (image URL)
{
"type": "HEADER",
"parameters": ["https://example.com/product-image.jpg"]
}

// Text header variable
{
"type": "HEADER",
"parameters": ["Special Campaign"]
}

BODY Component​

Used for dynamic text variables within the template body.

// 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"
}
}

BUTTONS Component​

Used for buttons with dynamic URL suffixes. If the template contains multiple URL buttons, only buttons with variables are indexed sequentially (buttons without variables or FLOW buttons are ignored).

// 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"]
}

For carousel templates, component variables are defined separately for each card.

{
"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"]
}
]
}
]
}
warning

The length of the cards array must exactly match the number of cards defined in the template.

Examples​

1. Simple Template — No Variables, Same Content for All Recipients​

This is the simplest way to send a static template without variables to multiple recipients.

{
"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" }
]
}'
tip
  • Recipients specified via username are automatically created if they do not already exist in the system
  • All recipients use the same defaultPayload; no recipient-specific data is required
  • When isMerge is not specified, each recipient directly uses the defaultPayload

2. Template with Shared Body Variables​

The same template is sent to all recipients using identical variable values. The template contains placeholders such as {{1}} and {{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" }]
}
tip
  • In this example, the template body might contain content such as: "Enjoy a {{1}} discount in our campaign! Offer valid until {{2}}."
  • parameters[0] replaces {{1}}, while parameters[1] replaces {{2}}

3. Personalized Template — Per-Recipient Variables with isMerge​

Different variable values are used for each recipient. By setting isMerge: true in the defaultPayload, recipients only need to provide variables; templateName and languageCode are inherited from the default payload.

In this example, the template includes both a media header and personalized body variables.

{
"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"]
}
]
}
}
}
]
}
tip
  • Thanks to isMerge: true, templateName does not need to be repeated in each recipient payload
  • Each recipient's variables array is completely independent
  • The header media URL must be publicly accessible
  • If type is omitted in the recipient payload, defaultPayload.type is inherited

4. URL Button Variable​

Use the BUTTONS component when the template contains a button with a dynamic URL suffix. For example, a tracking number appended to the base URL 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"]
}
]
}
}
}
]
}
tip
  • BUTTONS parameters are matched according to the order of buttons that contain variables
  • FLOW buttons and buttons without variables are ignored; indexing only applies to dynamic URL buttons
  • If the template button URL is https://example.com/track/{{1}}, then parameters[0] replaces {{1}}

5. Scheduled Delivery​

Schedule a message to be sent at a future date and time. scheduleAt must be provided as an ISO 8601 UTC timestamp.

{
"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" }
}
]
}
}
}
]
}
tip
  • scheduleAt must be provided in the UTC time zone (e.g., for 10:00 AM Turkey time, use 07:00:00.000Z)
  • This example uses the named parameter format; the template should contain placeholders such as {{name}} and {{discountRate}}
  • With action, conversations created by the message delivery are automatically assigned to the specified workgroup

Response​

A successful request returns a bulkOperationId, which can be used to track the delivery process:

{
"bulkOperationId": "abc123xyz789"
}

Error Responses​

StatusDescription
recipients field is requiredThe recipients field was not sent
Channel not foundNo verified channel matching channelUsername and connectorBrand was found
Channel is not verifiedThe matched channel exists but has not yet been verified
zod-schema-errorA field in the request body failed schema validation (missing required field, incorrect type, unsupported value, more than one identifier on a recipient, or a missing payload). The message names the field, e.g. recipients.0.referenceId: referenceId must be unique

The returned bulkOperationId can be used with the endpoints below to query delivery statistics and stop the delivery process.


Show — Bulk Operation Status and Statistics​

Retrieves the bulk operation with its current status and the delivery counters of its items, under the bulkOperation key.

POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/show.js

Request Parameters​

ParameterTypeRequiredDescription
bulkOperationIdstringYesID of the bulk operation to track (returned from 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"
}'

Response​

{
"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"
}
}

Show Fields​

The fields are under bulkOperation.

FieldTypeDescription
statusstringBulk operation status: preparing, scheduled, pending, running, stopping, stopped, completed, failed
progress.totalnumberTotal number of items
progress.pendingnumberItems still waiting in the queue
progress.succeedednumberItems successfully processed for delivery
progress.failednumberItems that encountered a delivery error
progress.cancellednumberItems cancelled by stop
progress.unknownnumberItems whose result could not be determined
result.outcomestringFinal outcome once the bulk operation has finished: success, partial, all-failed, cancelled, unknown
result.hasErrorsbooleantrue if at least one item failed
scheduleAtstringScheduled delivery time (only for scheduled bulk operations)
startedAt, completedAtstringWhen delivery started and finished
note

progress.succeeded indicates that the message was successfully handed off by the system. It does not guarantee that Meta delivered the message to the recipient. Subsequent status updates generated by Meta (such as delivered, read, or failed) do not affect this counter and are not reflected here.

Show vs Message Stats
  • show endpoint: Shows the delivery status of the items (pending, succeeded, failed, cancelled, unknown)
  • message-stats endpoint: Shows actual message status (pending, sending, sent, delivered, read, error, etc.)
  • A item may be succeeded, but the message might not yet be delivered/read

Reference ID​

The referenceId is an optional field that you can use to match delivery items with an ID from your own system for tracking purposes (e.g., order number, ticket ID). It is returned on each item as payload.referenceId.

If provided, it must be unique within the bulk (max 256 characters).

Important
  • Not required for use
  • Cannot be duplicated within the same bulk operation
  • Can be reused across different bulk operations

Message Stats — WhatsApp Message Status​

Retrieves the message status statistics for a bulk operation. Unlike the show endpoint, this shows the actual message.status of the sent messages instead of the item status.

Possible message statuses: 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

Request Parameters​

ParameterTypeRequiredDescription
bulkOperationIdstringYesID of the bulk operation to query
{
"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"
}'

Response​

{
"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
}

Message Stats Fields​

FieldTypeDescription
pendingnumberMessages not yet sent
sendingnumberMessages currently being sent
sentnumberMessages handed to WhatsApp, not yet delivered
deliverednumberMessages delivered to recipient's phone
readnumberMessages read by the recipient
deletednumberDeleted messages
warningnumberMessages with warnings
errornumberMessages with errors
unknownnumberMessages with unknown status
not-sendednumberMessages that could not be sent
no-messagenumberItems without a message (still queued, failed or cancelled)
totalnumberTotal item count
Usage Tips
  • A high no-message count while the bulk operation is running indicates delivery is still in progress
  • delivered count shows successful message delivery
  • read count can be used to measure campaign engagement
  • You can poll this endpoint periodically for real-time tracking

Options Structure — Pagination, Filtering, and Sorting​

The options parameter used in list and list-with-messages endpoints provides a standard structure for pagination, filtering, and sorting.

Options Fields​

FieldTypeRequiredDescription
paginationobjectNoPagination settings
filteringobjectNoFiltering criteria
sortingobjectNoSorting settings (e.g., {"position": 1})

Pagination Structure​

If pagination is not sent, the first page is returned with up to 50 items. If it is sent, currentPage is required.

FieldTypeDefaultDescription
currentPagenumber—Page number to retrieve (required when pagination is sent)
pageItemsnumber50Number of items per page (maximum: 50)

Filtering Structure​

Filtering is done using MongoDB query format on the item fields, using the field names exactly as they appear in the response (e.g. status, payload.referenceId, result.messageId). In the list-with-messages endpoint, you can also filter by message fields (e.g., 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
}
}
}
}

Sorting Structure​

Sorting is done using MongoDB sort format. 1 means ascending, -1 means descending. position is the order of the recipient in the recipients array of the request (starting from 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
}
}
}

Combined Usage Example​

{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 2,
"pageItems": 50
},
"filtering": {
"message.status": "delivered"
},
"sorting": {
"position": 1
}
}
}

This example lists the second page of delivered messages in recipient order.

Practical Tips
  • Combine filtering and pagination for targeted queries
  • Response includes options.pagination.totalCount and options.pagination.totalPages for navigation
  • message.status filter only works in the list-with-messages endpoint

Items — Item Details and Listing​

Lists all delivery items for a bulk operation. Each item represents one entry of the recipients array.

List — Item List​

POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation-items/list.js

Request Parameters​

ParameterTypeRequiredDescription
bulkOperationIdstringYesID of the bulk operation to list
optionsobjectNoPagination, filtering, sorting options. See Options Structure for details.

Request — Basic Usage​

{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
}
}
}

Request — Filter by Specific Status​

{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
},
"filtering": {
"status": "failed"
}
}
}

Lists only failed delivery items, with the reason in error.

Response​

{
"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 — List with Message Details​

Has the same features as the list endpoint (pagination, filtering, sorting). The difference: each item also carries the related message and session objects when they exist. This allows you to see the message status (delivered, read, error, etc.) and payload in a single query.

POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation-items/list-with-messages.js

Request — Basic Usage​

{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
}
}
}

Request — Filter by Message Status​

{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
},
"filtering": {
"message.status": "delivered"
}
}
}

This example fetches only messages with "delivered" status.

Request — List Failed Messages​

{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
},
"filtering": {
"message.status": "error"
}
}
}

Used to review messages that encountered delivery errors.

Response​

{
"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": {}
}
}

Item Fields​

FieldTypeDescription
_idstringItem ID
bulkOperationIdstringBulk operation ID this item belongs to
positionnumberOrder of the recipient in the request's recipients array, starting from 0
payloadobjectThe recipient exactly as sent in the request (username / userId / sessionId, referenceId, payload, isMerge)
statusstringItem status: pending, succeeded, failed, cancelled, unknown
errorobjectOnly on failed items: code, message and retryable
result.userIdstringRecipient user ID
result.sessionIdstringConversation session ID
result.messageIdstringSent message ID (missing if no message was sent)
completedAtstringWhen the item was processed
messageobjectMessage details (only in list-with-messages)
sessionobjectConversation details (only in list-with-messages)

List vs List-With-Messages Comparison​

Featurelistlist-with-messages
PaginationYesYes
FilteringYes (item fields only)Yes (item + message fields)
Message / SessionNoYes (if they exist)
PerformanceFastMedium (performs JOIN)
message.status FilterNot availableAvailable
Usage Scenarios
  • list: Quickly list all items, filter by referenceId, check item statuses
  • list-with-messages: View WhatsApp message statuses (delivered, read), inspect message payloads
  • Filtering examples:
    • {"status": "failed"} — Failed delivery items
    • {"payload.referenceId": "ORDER-123"} — Specific reference ID
    • {"message.status": "delivered"} — Delivered messages (list-with-messages only)
    • {"message.status": "read"} — Read messages (list-with-messages only)
    • {"message.status": "error"} — Messages with errors (list-with-messages only)
    • {"message.status": {"$in": ["delivered", "read"]}} — Messages that are either delivered or read (list-with-messages only)
    • {"message.status": {"$nin": ["error", "not-sended"]}} — Messages excluding errors and not-sent (list-with-messages only)
    • {"message": {"$exists": false}} — Items without a message (list-with-messages only)
    • {"status": {"$in": ["succeeded", "pending"]}} — Items that are successful or pending
Performance and Usage Notes
  • list endpoint is faster; prefer it for general status checks
  • Use list-with-messages when message details are needed
  • Maximum items per page (pageItems) is 50; defaults to 50 if not specified
  • message.status filter only works in list-with-messages
  • Items without result.messageId have no message object

Stop — Cancel Delivery​

Stops an ongoing bulk operation delivery. All pending items are moved to the cancelled status and the bulk operation becomes stopped. Items that have already been processed as succeeded or failed are not affected. A scheduled bulk operation can also be stopped before its scheduleAt time.

POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/stop.js
warning

This action cannot be undone. A stopped delivery cannot be resumed.

Request Parameters​

ParameterTypeRequiredDescription
bulkOperationIdstringYesID of the bulk operation to stop
{
"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"
}'

Response​

For a successful request, the response body is empty. An HTTP 200 status indicates that the operation completed successfully.

{}
tip
  • Only bulk operations created through this API can be stopped
  • After stopping a delivery, you can use the show endpoint to verify the number of cancelled items (bulkOperation.progress.cancelled)
  • If the bulk operation cannot be found, the API returns the error: Bulk operation not found.