مستندات Bulk Template Message API — v2
این API به شما امکان میدهد پیامهای قالب را همزمان از طریق کانال WhatsApp برای چند گیرنده ارسال کنید.
ارسال پیامها بهصورت ناهمگام (asynchronous) پردازش میشود؛ درخواست API بلافاصله یک bulkOperationId برمیگرداند و پیامها در پسزمینه صفبندی و پردازش میشوند.
POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/send.js
هر اندپوینت در این سند یک تابع سفارشی (custom function) است. بدنهٔ پاسخ HTTP فیلدهای خود تابع (_id، title، pathString…) را دارد و پاسخ واقعی اندپوینت زیر result قرار میگیرد. نمونههای پاسخ در این سند محتوای result را نشان میدهند:
{
"_id": "function_id",
"title": "Show",
"pathString": "/bulk-message-api-app/api/v2/bulk-operation/show.js",
"result": {
"bulkOperation": { "_id": "abc123xyz789", "status": "running" }
}
}
مهاجرت از v1
v2 در کنار v1 کار میکند، بنابراین میتوانید طبق برنامهٔ خودتان مهاجرت کنید. اندپوینتهای v1 (/api/bulk-message/…، /api/bulk-message-records/…) کنار گذاشته خواهند شد؛ یکپارچهسازیهای جدید باید از v2 استفاده کنند.
- مسیرهای اندپوینت را به v2 منتقل کنید:
…/api/bulk-message/…به…/api/v2/bulk-operation/…و…/api/bulk-message-records/…به…/api/v2/bulk-operation-items/…تبدیل میشود. بدنهٔ درخواستsendتغییری نمیکند؛ اکنون یک درخواست بهجای ۵٬۰۰۰ گیرنده تا ۱۰۰٬۰۰۰ گیرنده را میپذیرد. - در درخواستها بهجای
bulkMessageId،bulkOperationIdرا ارسال کنید. stats.jsرا باshow.jsجایگزین کنید.- پاسخها را با نامهای جدید فیلدها در جدول زیر بخوانید.
اندپوینتهای v2 فقط عملیاتهای گروهیای را میشناسند که با send نسخهٔ v2 ایجاد شدهاند. برای پیگیری پیام گروهیای که پیش از مهاجرت با v1 ایجاد کردهاید، برای آن همچنان از اندپوینتهای v1 استفاده کنید.
اندپوینتها
| v1 | v2 | چه چیزی در پاسخ تغییر میکند |
|---|---|---|
bulk-message/send.js | v2/bulk-operation/send.js | { bulkMessageId } به { bulkOperationId } تبدیل میشود |
bulk-message/stats.js | v2/bulk-operation/show.js | { bulkMessage, stats } به { bulkOperation } تبدیل میشود |
bulk-message/message-stats.js | v2/bulk-operation/message-stats.js | شمارندهها بهجای messageStats در ریشه قرار دارند |
bulk-message/stop.js | v2/bulk-operation/stop.js | هیچ |
bulk-message-records/list.js | v2/bulk-operation-items/list.js | bulkMessageRecords به bulkOperationItems تبدیل میشود |
bulk-message-records/list-with-messages.js | v2/bulk-operation-items/list-with-messages.js | مانند list، و هر آیتم session را هم دارد |
نام فیلدها
| 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 (پیام گروهی) | bulkOperation (عملیات گروهی) |
bulkMessageRecord (رکورد) | bulkOperationItem (آیتم) |
bulkMessageId در درخواستها، در پاسخ send و در رکوردها | bulkOperationId |
username / userId / sessionId رکورد، همانطور که ارسال شده | payload.username / payload.userId / payload.sessionId |
referenceId رکورد | payload.referenceId |
payload رکورد (payload قالب گیرنده) | payload.payload |
userId، sessionId، messageId رکورد پس از ارسال | result.userId، result.sessionId، result.messageId |
وضعیت رکورد success | succeeded |
errorMessage در رکوردهای ناموفق | error.message، در کنار error.code و error.retryable |
| — | position (اندیس گیرنده در recipients) |
فیلتر {"status": "success"} | {"status": "succeeded"} |
فیلتر {"referenceId": "…"} | {"payload.referenceId": "…"} |
خطاهای schema-error و invalidRequest در send | zod-schema-error، همراه با مسیر فیلد نامعتبر در پیام |
ویژگیهای کلیدی
- ارسال پیامهای قالب گروهی به ۱ تا ۱۰۰٬۰۰۰ گیرنده در یک درخواست
- متغیرهای شخصیسازیشده برای هر گیرنده (HEADER، BODY، BUTTONS، CAROUSEL)
defaultPayloadمشترک برای همهٔ گیرندگان و ادغامpayloadاختصاصی هر گیرنده (isMerge)- ارسال زمانبندیشده (
scheduleAt) - مشخصکردن گیرندگان با
username(شمارهٔ تلفن)،userIdیاsessionId - ارسال چند پیام به یک کاربر در یک درخواست گروهی
referenceIdاختیاری برای پیگیری رکوردها در سیستمهای بیرونی- واگذاری خودکار گفتگوها به یک کاربر یا گروه کاری پس از ارسال (
action)
این API فقط میتواند قالبهایی را ارسال کند که توسط Meta تأیید شدهاند.
نام قالب و کد زبان باید دقیقاً با قالب ثبتشده در Meta Business Manager مطابقت داشته باشند.
پارامترهای درخواست
پارامترهای سطح بالا
| پارامتر | نوع | الزامی | توضیحات |
|---|---|---|---|
channelUsername | string | بله | شمارهٔ کانال WhatsApp که برای ارسال پیام استفاده میشود (مثلاً "908506669933") |
connectorBrand | string | بله | برند اتصالدهندهٔ کانال. مقدار پشتیبانیشدهٔ فعلی: "whatsapp" |
title | string | خیر | عنوان کمپین. برای نمایش در داشبورد استفاده میشود. پیشفرض: "-" |
scheduleAt | string (ISO 8601) | خیر | تاریخ و زمان ارسال زمانبندیشده (مثلاً "2026-06-10T10:00:00.000Z"). اگر ذکر نشود، پیامها بلافاصله ارسال میشوند. |
defaultPayload | object | مشروط* | payload پیشفرض قالب که برای همهٔ گیرندگان اعمال میشود. اگر گیرندهای payload اختصاصی خودش را نداشته باشد، الزامی است. |
recipients | array | بله | فهرست گیرندگان. حداقل ۱ و حداکثر ۱۰۰٬۰۰۰ گیرنده. |
action | object | خیر | اقدامی که پس از ارسال روی گفتگو اعمال میشود. assign-to-user یا assign-to-workgroup. |
*اگر همهٔ گیرندگان payload اختصاصی خودشان را داشته باشند، defaultPayload اختیاری است.
ساختار گیرنده
برای هر گیرنده باید دقیقاً یکی از username، userId یا sessionId داده شود. چند شناسه را نمیتوان همزمان به کار برد و ندادن هیچکدام به خطا منجر میشود.
برای ارسال چند پیام به یک کاربر در یک درخواست گروهی، همان شناسه را چند بار به فهرست گیرندگان اضافه کنید.
| فیلد | نوع | توضیحات |
|---|---|---|
username | string | شمارهٔ تلفن گیرنده (مثلاً "905554443322"). اگر کاربر در سیستم وجود نداشته باشد، بهصورت خودکار ساخته میشود. |
userId | string | شناسهٔ یک کاربر موجود در سیستم. |
sessionId | string | شناسهٔ یک نشست گفتگوی موجود در سیستم. |
referenceId | string | شناسهٔ مرجع اختیاری (حداکثر ۲۵۶ نویسه) برای تطبیق با رکوردهای سیستم بیرونی (CRM، سفارش، تیکت و غیره). در صورت ذکر، باید در همان درخواست گروهی یکتا باشد. |
payload | object | payload قالب اختصاصی گیرنده (اختیاری). اگر isMerge: true باشد، با defaultPayload ادغام میشود. |
isMerge | boolean | رفتار ادغام را برای این گیرنده بازنویسی میکند (اختیاری). اگر ذکر نشود، defaultPayload.isMerge استفاده میشود. |
ساختار اقدام
| فیلد | نوع | توضیحات |
|---|---|---|
action.key | string | "assign-to-user" گفتگو را به یک کارشناس مشخص و "assign-to-workgroup" آن را به یک گروه کاری واگذار میکند. |
action.value | string | شناسهٔ کاربر یا گروه کاری مقصد. |
ساختار Payload قالب
defaultPayload و payload اختصاصی گیرنده ساختار یکسانی دارند. برای پیامهای قالب باید type: "template" مشخص شود.
{
"type": "template",
"template": {
"templateName": "order_confirmation",
"languageCode": "tr-TR",
"variables": []
},
"isMerge": true
}
| فیلد | نوع | الزامی | توضیحات |
|---|---|---|---|
type | string | بله | نوع payload. برای پیامهای قالب: "template" |
template.templateName | string | بله* | نام قالب ثبتشده در Meta. وقتی isMerge: true به کار رود، در payload گیرندگان اختیاری است (از payload پیشفرض به ارث میرسد). |
template.languageCode | string | خیر | کد زبان قالب (مثلاً "tr"، "en"، "tr-TR"، "en-US"). باید دقیقاً با زبان ثبتشده در Meta مطابقت داشته باشد. |
template.variables | array | خیر | فهرست متغیرهایی که روی اجزای قالب اعمال میشوند. جزئیات در ادامه آمده است. |
isMerge | boolean | خیر | true: payload گیرنده بهصورت عمیق با defaultPayload ادغام میشود. false یا ذکرنشده: payload گیرنده بهطور کامل جایگزین defaultPayload میشود. |
- اگر
defaultPayload.isMerge: trueباشد و گیرندهpayloadاختصاصی خودش را بدهد، ادغام عمیق اعمال میشود. - در ادغام عمیق، گیرنده میتواند فقط
variablesرا بدهد؛templateNameوlanguageCodeاز payload پیشفرض به ارث میرسند. - فیلد
isMergeگیرنده،defaultPayload.isMergeرا بازنویسی میکند. - اگر گیرنده
payloadندهد، همیشهdefaultPayloadاستفاده میشود.
ساختار متغیرها
هر مورد از آرایهٔ variables با یک جزء قالب متناظر است. نوع هر جزء (type) فقط یک بار میتواند در آرایه بیاید.
| type | توضیحات | parameters | cards |
|---|---|---|---|
"HEADER" | جزء سرایند قالب. متغیر متنی یا نشانی رسانه (تصویر، ویدیو، سند) | بله | — |
"BODY" | متغیرهای متنی بدنهٔ قالب | بله | — |
"BUTTONS" | متغیرهای پسوند URL پویا برای دکمهها | بله | — |
"CAROUSEL" | اجزای کارت برای قالبهای کاروسل | — | بله |
قالبهای پارامتر
فیلد parameters را میتوان در سه قالب مختلف ارائه کرد:
// 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" }
- اگر قالب Meta شما جانشینهایی مانند
{{1}}و{{2}}دارد، از قالب آرایه یا قالب شیء با کلید عددی استفاده کنید. - اگر قالب شما جانشینهای نامدار مانند
{{firstName}}یا{{orderTotal}}دارد، از قالب شیء با کلید نامدار استفاده کنید. - هنگام استفاده از آرایه، ترتیب مهم است: عنصر اول به
{{1}}، عنصر دوم به{{2}}و به همین ترتیب نگاشت میشود.
جزء HEADER
وقتی سرایند قالب شامل رسانه (تصویر، ویدیو، سند) یا متن پویا است، از جزء HEADER استفاده کنید.
// Media header (image URL)
{
"type": "HEADER",
"parameters": ["https://example.com/product-image.jpg"]
}
// Text header variable
{
"type": "HEADER",
"parameters": ["Special Campaign"]
}
جزء 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
برای دکمههایی با پسوند URL پویا استفاده میشود. اگر قالب چند دکمهٔ URL داشته باشد، فقط دکمههای دارای متغیر بهترتیب نمایهگذاری میشوند (دکمههای بدون متغیر یا دکمههای FLOW نادیده گرفته میشوند).
// 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"]
}
جزء CAROUSEL
برای قالبهای کاروسل، متغیرهای اجزا برای هر کارت جداگانه تعریف میشوند.
{
"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"]
}
]
}
]
}
طول آرایهٔ cards باید دقیقاً با تعداد کارتهای تعریفشده در قالب برابر باشد.
نمونهها
1. قالب ساده — بدون متغیر، محتوای یکسان برای همهٔ گیرندگان
این سادهترین روش برای ارسال یک قالب ثابت و بدون متغیر به چند گیرنده است.
{
"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" }
]
}'
- گیرندگانی که با
usernameمشخص شدهاند، اگر هنوز در سیستم وجود نداشته باشند بهصورت خودکار ساخته میشوند - همهٔ گیرندگان از
defaultPayloadیکسانی استفاده میکنند؛ هیچ دادهای اختصاصی گیرنده لازم نیست - وقتی
isMergeذکر نشود، هر گیرنده مستقیماً ازdefaultPayloadاستفاده میکند
2. قالب با متغیرهای مشترک بدنه
یک قالب با مقادیر متغیر یکسان برای همهٔ گیرندگان ارسال میشود. قالب شامل جانشینهایی مانند {{1}} و {{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" }]
}
- در این مثال، بدنهٔ قالب ممکن است محتوایی مانند این داشته باشد: «از تخفیف
{{1}}در کمپین ما بهرهمند شوید! پیشنهاد تا{{2}}معتبر است.» parameters[0]جایگزین{{1}}وparameters[1]جایگزین{{2}}میشود
3. قالب شخصیسازیشده — متغیرهای اختصاصی هر گیرنده با isMerge
برای هر گیرنده مقادیر متغیر متفاوتی استفاده میشود. با تنظیم isMerge: true در defaultPayload، گیرندگان فقط باید variables را بدهند؛ templateName و languageCode از payload پیشفرض به ارث میرسند.
در این مثال، قالب هم سرایند رسانهای دارد و هم متغیرهای شخصیسازیشدهٔ بدنه.
{
"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"]
}
]
}
}
}
]
}
- به لطف
isMerge: true، لازم نیستtemplateNameدر payload هر گیرنده تکرار شود - آرایهٔ
variablesهر گیرنده کاملاً مستقل است - نشانی رسانهٔ سرایند باید بهصورت عمومی در دسترس باشد
- اگر
typeدر payload گیرنده ذکر نشود،defaultPayload.typeبه ارث میرسد
4. متغیر دکمهٔ URL
وقتی قالب دکمهای با پسوند URL پویا دارد، از جزء BUTTONS استفاده کنید. برای مثال، شمارهٔ رهگیری که به نشانی پایهٔ 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"]
}
]
}
}
}
]
}
- پارامترهای
BUTTONSبر اساس ترتیب دکمههایی که متغیر دارند تطبیق داده میشوند - دکمههای FLOW و دکمههای بدون متغیر نادیده گرفته میشوند؛ نمایهگذاری فقط برای دکمههای URL پویا اعمال میشود
- اگر URL دکمهٔ قالب
https://example.com/track/{{1}}باشد،parameters[0]جایگزین{{1}}میشود
5. ارسال زمانبندیشده
ارسال یک پیام را برای تاریخ و زمانی در آینده زمانبندی کنید. scheduleAt باید بهصورت زماننگاشت 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" }
}
]
}
}
}
]
}
scheduleAtباید در منطقهٔ زمانی UTC داده شود (مثلاً برای ساعت ۱۰:۰۰ صبح به وقت ترکیه، از07:00:00.000Zاستفاده کنید)- این مثال از قالب پارامتر نامدار استفاده میکند؛ قالب باید جانشینهایی مانند
{{name}}و{{discountRate}}داشته باشد - با
action، گفتگوهایی که در اثر ارسال پیام ایجاد میشوند بهصورت خودکار به گروه کاری مشخصشده واگذار میشوند
پاسخ
یک درخواست موفق bulkOperationId را برمیگرداند که میتوان از آن برای پیگیری روند ارسال استفاده کرد:
{
"bulkOperationId": "abc123xyz789"
}
پاسخهای خطا
| وضعیت | توضیحات |
|---|---|
recipients field is required | فیلد recipients ارسال نشده است |
Channel not found | هیچ کانال تأییدشدهای مطابق با channelUsername و connectorBrand پیدا نشد |
Channel is not verified | کانال مطابق وجود دارد اما هنوز تأیید نشده است |
zod-schema-error | یکی از فیلدهای بدنهٔ درخواست در اعتبارسنجی schema رد شد (فیلد الزامی جاافتاده، نوع نادرست، مقدار پشتیبانینشده، بیش از یک شناسه برای یک گیرنده، یا نبودِ payload). پیام نام فیلد را ذکر میکند، مثلاً recipients.0.referenceId: referenceId must be unique |
bulkOperationId برگشتی را میتوان با اندپوینتهای زیر برای پرسوجوی آمار ارسال و متوقفکردن روند ارسال به کار برد.
Show — وضعیت و آمار عملیات گروهی
عملیات گروهی را همراه با وضعیت فعلی آن و شمارندههای ارسال آیتمهایش، زیر کلید bulkOperation دریافت میکند.
POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/show.js
پارامترهای درخواست
| پارامتر | نوع | الزامی | توضیحات |
|---|---|---|---|
bulkOperationId | string | بله | شناسهٔ عملیات گروهی برای پیگیری (برگشتی از 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"
}'
پاسخ
{
"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
فیلدها زیر bulkOperation قرار دارند.
| فیلد | نوع | توضیحات |
|---|---|---|
status | string | وضعیت عملیات گروهی: preparing، scheduled، pending، running، stopping، stopped، completed، failed |
progress.total | number | تعداد کل آیتمها |
progress.pending | number | آیتمهایی که هنوز در صف منتظرند |
progress.succeeded | number | آیتمهایی که با موفقیت برای ارسال پردازش شدهاند |
progress.failed | number | آیتمهایی که با خطای ارسال مواجه شدهاند |
progress.cancelled | number | آیتمهایی که با stop لغو شدهاند |
progress.unknown | number | آیتمهایی که نتیجهٔ آنها مشخص نشد |
result.outcome | string | نتیجهٔ نهایی پس از پایان عملیات گروهی: success، partial، all-failed، cancelled، unknown |
result.hasErrors | boolean | اگر دستکم یک آیتم ناموفق باشد true است |
scheduleAt | string | زمان ارسال زمانبندیشده (فقط برای عملیاتهای گروهی زمانبندیشده) |
startedAt، completedAt | string | زمان شروع و پایان ارسال |
progress.succeeded نشان میدهد که پیام با موفقیت توسط سیستم تحویل داده شده است. این تضمین نمیکند که Meta پیام را به گیرنده رسانده باشد. بهروزرسانیهای بعدی وضعیت که Meta ایجاد میکند (مانند delivered، read یا failed) روی این شمارنده اثری ندارند و اینجا منعکس نمیشوند.
- اندپوینت show: وضعیت ارسال آیتمها را نشان میدهد (
pending،succeeded،failed،cancelled،unknown) - اندپوینت message-stats: وضعیت واقعی پیام را نشان میدهد (
pending،sending،sent،delivered،read،errorو غیره) - ممکن است یک آیتم
succeededباشد اما پیام هنوز تحویل داده یا خوانده نشده باشد
شناسهٔ مرجع
referenceId یک فیلد اختیاری است که میتوانید برای پیگیری از آن استفاده کنید تا آیتمهای ارسال را با شناسهای از سیستم خودتان (مثلاً شمارهٔ سفارش یا شناسهٔ تیکت) تطبیق دهید. این فیلد در هر آیتم بهصورت payload.referenceId برگردانده میشود.
در صورت ذکر، باید در همان درخواست گروهی یکتا باشد (حداکثر ۲۵۶ نویسه).
- استفاده از آن الزامی نیست
- در یک عملیات گروهی نمیتواند تکراری باشد
- میتوان آن را در عملیاتهای گروهی مختلف دوباره استفاده کرد
Message Stats — وضعیت پیام WhatsApp
آمار وضعیت پیامهای یک عملیات گروهی را دریافت میکند. برخلاف اندپوینت show، این اندپوینت بهجای وضعیت آیتم، message.status واقعی پیامهای ارسالشده را نشان میدهد.
وضعیتهای ممکن پیام: 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
پارامترهای درخواست
| پارامتر | نوع | الزامی | توضیحات |
|---|---|---|---|
bulkOperationId | string | بله | شناسهٔ عملیات گروهی برای پرسوجو |
{
"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"
}'
پاسخ
{
"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
| فیلد | نوع | توضیحات |
|---|---|---|
pending | number | پیامهایی که هنوز ارسال نشدهاند |
sending | number | پیامهایی که در حال ارسالاند |
sent | number | پیامهایی که به WhatsApp تحویل شدهاند اما هنوز به مقصد نرسیدهاند |
delivered | number | پیامهایی که به گوشی گیرنده رسیدهاند |
read | number | پیامهایی که گیرنده خوانده است |
deleted | number | پیامهای حذفشده |
warning | number | پیامهای دارای هشدار |
error | number | پیامهای دارای خطا |
unknown | number | پیامهایی با وضعیت نامشخص |
not-sended | number | پیامهایی که ارسال نشدند |
no-message | number | آیتمهای بدون پیام (هنوز در صف، ناموفق یا لغوشده) |
total | number | تعداد کل آیتمها |
- عدد بالای
no-messageدر حالی که عملیات گروهیrunningاست، نشان میدهد ارسال هنوز در جریان است - عدد
deliveredتحویل موفق پیامها را نشان میدهد - از عدد
readمیتوان برای سنجش میزان تعامل با کمپین استفاده کرد - برای پیگیری لحظهای میتوانید این اندپوینت را بهصورت دورهای فراخوانی کنید
ساختار Options — صفحهبندی، فیلتر و مرتبسازی
پارامتر options که در اندپوینتهای list و list-with-messages استفاده میشود، ساختاری استاندارد برای صفحهبندی، فیلتر و مرتبسازی فراهم میکند.
فیلدهای Options
| فیلد | نوع | الزامی | توضیحات |
|---|---|---|---|
pagination | object | خیر | تنظیمات صفحهبندی |
filtering | object | خیر | معیارهای فیلتر |
sorting | object | خیر | تنظیمات مرتبسازی (مثلاً {"position": 1}) |
ساختار صفحهبندی
اگر pagination ارسال نشود، صفحهٔ اول با حداکثر ۵۰ آیتم برگردانده میشود. اگر ارسال شود، currentPage الزامی است.
| فیلد | نوع | پیشفرض | توضیحات |
|---|---|---|---|
currentPage | number | — | شمارهٔ صفحهای که باید دریافت شود (هنگام ارسال pagination الزامی است) |
pageItems | number | 50 | تعداد آیتم در هر صفحه (حداکثر: ۵۰) |
ساختار فیلتر
فیلتر با قالب کوئری MongoDB روی فیلدهای آیتم انجام میشود و نام فیلدها دقیقاً همانطور که در پاسخ آمدهاند به کار میروند (مثلاً status، payload.referenceId، result.messageId). در اندپوینت list-with-messages میتوانید بر اساس فیلدهای پیام (مثلاً 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
}
}
}
}
ساختار مرتبسازی
مرتبسازی با قالب sort در MongoDB انجام میشود. 1 یعنی صعودی و -1 یعنی نزولی. position ترتیب گیرنده در آرایهٔ recipients درخواست است (از 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
}
}
}
نمونهٔ استفادهٔ ترکیبی
{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 2,
"pageItems": 50
},
"filtering": {
"message.status": "delivered"
},
"sorting": {
"position": 1
}
}
}
این نمونه صفحهٔ دوم پیامهای تحویلشده را به ترتیب گیرندگان فهرست میکند.
- برای کوئریهای هدفمند،
filteringوpaginationرا با هم ترکیب کنید - پاسخ برای پیمایش،
options.pagination.totalCountوoptions.pagination.totalPagesرا دارد - فیلتر
message.statusفقط در اندپوینتlist-with-messagesکار میکند
Items — جزئیات و فهرست آیتمها
همهٔ آیتمهای ارسال یک عملیات گروهی را فهرست میکند. هر آیتم نمایندهٔ یک ورودی از آرایهٔ recipients است.
List — فهرست آیتمها
POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation-items/list.js
پارامترهای درخواست
| پارامتر | نوع | الزامی | توضیحات |
|---|---|---|---|
bulkOperationId | string | بله | شناسهٔ عملیات گروهی برای فهرستکردن |
options | object | خیر | گزینههای صفحهبندی، فیلتر و مرتبسازی. برای جزئیات ساختار Options را ببینید. |
درخواست — استفادهٔ پایه
{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
}
}
}
درخواست — فیلتر بر اساس یک وضعیت مشخص
{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
},
"filtering": {
"status": "failed"
}
}
}
فقط آیتمهای ارسال ناموفق را همراه با علت در error فهرست میکند.
پاسخ
{
"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 (صفحهبندی، فیلتر، مرتبسازی) را دارد. تفاوت: هر آیتم در صورت وجود، اشیای مرتبط message و session را هم دارد. به این ترتیب میتوانید وضعیت پیام (delivered، read، error و غیره) و payload آن را در یک کوئری ببینید.
POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation-items/list-with-messages.js
درخواست — استفادهٔ پایه
{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
}
}
}
درخواست — فیلتر بر اساس وضعیت پیام
{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
},
"filtering": {
"message.status": "delivered"
}
}
}
این نمونه فقط پیامهای با وضعیت "delivered" را دریافت میکند.
درخواست — فهرست پیامهای ناموفق
{
"bulkOperationId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
},
"filtering": {
"message.status": "error"
}
}
}
برای بررسی پیامهایی که با خطای ارسال مواجه شدهاند استفاده میشود.
پاسخ
{
"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": {}
}
}
فیلدهای آیتم
| فیلد | نوع | توضیحات |
|---|---|---|
_id | string | شناسهٔ آیتم |
bulkOperationId | string | شناسهٔ عملیات گروهیای که این آیتم به آن تعلق دارد |
position | number | ترتیب گیرنده در آرایهٔ recipients درخواست، از 0 |
payload | object | گیرنده دقیقاً همانطور که در درخواست ارسال شده (username / userId / sessionId، referenceId، payload، isMerge) |
status | string | وضعیت آیتم: pending، succeeded، failed، cancelled، unknown |
error | object | فقط در آیتمهای failed: code، message و retryable |
result.userId | string | شناسهٔ کاربر گیرنده |
result.sessionId | string | شناسهٔ نشست گفتگو |
result.messageId | string | شناسهٔ پیام ارسالشده (اگر پیامی ارسال نشده باشد وجود ندارد) |
completedAt | string | زمان پردازش آیتم |
message | object | جزئیات پیام (فقط در list-with-messages) |
session | object | جزئیات گفتگو (فقط در list-with-messages) |
مقایسهٔ List و List-With-Messages
| ویژگی | list | list-with-messages |
|---|---|---|
| صفحهبندی | بله | بله |
| فیلتر | بله (فقط فیلدهای آیتم) | بله (فیلدهای آیتم + پیام) |
| پیام / نشست | خیر | بله (در صورت وجود) |
| کارایی | سریع | متوسط (JOIN انجام میدهد) |
| فیلتر message.status | در دسترس نیست | در دسترس است |
- list: فهرست سریع همهٔ آیتمها، فیلتر بر اساس referenceId، بررسی وضعیت آیتمها
- list-with-messages: مشاهدهٔ وضعیت پیامهای WhatsApp (delivered، read)، بررسی payload پیامها
- نمونههای فیلتر:
{"status": "failed"}— آیتمهای ارسال ناموفق{"payload.referenceId": "ORDER-123"}— یک شناسهٔ مرجع مشخص{"message.status": "delivered"}— پیامهای تحویلشده (فقط list-with-messages){"message.status": "read"}— پیامهای خواندهشده (فقط list-with-messages){"message.status": "error"}— پیامهای دارای خطا (فقط list-with-messages){"message.status": {"$in": ["delivered", "read"]}}— پیامهایی که تحویل یا خوانده شدهاند (فقط list-with-messages){"message.status": {"$nin": ["error", "not-sended"]}}— پیامها بهجز موارد خطا و ارسالنشده (فقط list-with-messages){"message": {"$exists": false}}— آیتمهای بدون پیام (فقط list-with-messages){"status": {"$in": ["succeeded", "pending"]}}— آیتمهای موفق یا در انتظار
- اندپوینت
listسریعتر است؛ برای بررسیهای کلی وضعیت آن را ترجیح دهید - وقتی به جزئیات پیام نیاز دارید از
list-with-messagesاستفاده کنید - حداکثر آیتم در هر صفحه (pageItems) ۵۰ است؛ اگر ذکر نشود پیشفرض ۵۰ است
- فیلتر
message.statusفقط درlist-with-messagesکار میکند - آیتمهایی که
result.messageIdندارند، شیءmessageهم ندارند
Stop — لغو ارسال
ارسال در حال انجام یک عملیات گروهی را متوقف میکند. همهٔ آیتمهای pending به وضعیت cancelled منتقل میشوند و عملیات گروهی stopped میشود. آیتمهایی که پیشتر بهصورت succeeded یا failed پردازش شدهاند تحت تأثیر قرار نمیگیرند. یک عملیات گروهی زمانبندیشده را هم میتوان پیش از زمان scheduleAt آن متوقف کرد.
POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/v2/bulk-operation/stop.js
این اقدام قابل بازگشت نیست. ارسال متوقفشده را نمیتوان از سر گرفت.
پارامترهای درخواست
| پارامتر | نوع | الزامی | توضیحات |
|---|---|---|---|
bulkOperationId | string | بله | شناسهٔ عملیات گروهی برای توقف |
{
"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"
}'
پاسخ
برای یک درخواست موفق، بدنهٔ پاسخ خالی است. وضعیت HTTP 200 نشان میدهد عملیات با موفقیت انجام شده است.
{}
- فقط عملیاتهای گروهیای که از طریق این API ایجاد شدهاند قابل توقفاند
- پس از توقف ارسال، میتوانید با اندپوینت
showتعداد آیتمهای لغوشده (bulkOperation.progress.cancelled) را بررسی کنید - اگر عملیات گروهی پیدا نشود، API این خطا را برمیگرداند:
Bulk operation not found.