پرش به مطلب اصلی

مستندات 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 استفاده کنند.

  1. مسیرهای اندپوینت را به v2 منتقل کنید: …/api/bulk-message/… به …/api/v2/bulk-operation/… و …/api/bulk-message-records/… به …/api/v2/bulk-operation-items/… تبدیل می‌شود. بدنهٔ درخواست send تغییری نمی‌کند؛ اکنون یک درخواست به‌جای ۵٬۰۰۰ گیرنده تا ۱۰۰٬۰۰۰ گیرنده را می‌پذیرد.
  2. در درخواست‌ها به‌جای bulkMessageId، bulkOperationId را ارسال کنید.
  3. stats.js را با show.js جایگزین کنید.
  4. پاسخ‌ها را با نام‌های جدید فیلدها در جدول زیر بخوانید.

اندپوینت‌های v2 فقط عملیات‌های گروهی‌ای را می‌شناسند که با send نسخهٔ v2 ایجاد شده‌اند. برای پیگیری پیام گروهی‌ای که پیش از مهاجرت با v1 ایجاد کرده‌اید، برای آن همچنان از اندپوینت‌های v1 استفاده کنید.

اندپوینت‌ها​

v1v2چه چیزی در پاسخ تغییر می‌کند
bulk-message/send.jsv2/bulk-operation/send.js{ bulkMessageId } به { bulkOperationId } تبدیل می‌شود
bulk-message/stats.jsv2/bulk-operation/show.js{ bulkMessage, stats } به { bulkOperation } تبدیل می‌شود
bulk-message/message-stats.jsv2/bulk-operation/message-stats.jsشمارنده‌ها به‌جای messageStats در ریشه قرار دارند
bulk-message/stop.jsv2/bulk-operation/stop.jsهیچ
bulk-message-records/list.jsv2/bulk-operation-items/list.jsbulkMessageRecords به bulkOperationItems تبدیل می‌شود
bulk-message-records/list-with-messages.jsv2/bulk-operation-items/list-with-messages.jsمانند list، و هر آیتم session را هم دارد

نام فیلدها​

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 (پیام گروهی)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
وضعیت رکورد successsucceeded
errorMessage در رکوردهای ناموفقerror.message، در کنار error.code و error.retryable
—position (اندیس گیرنده در recipients)
فیلتر {"status": "success"}{"status": "succeeded"}
فیلتر {"referenceId": "…"}{"payload.referenceId": "…"}
خطاهای schema-error و invalidRequest در sendzod-schema-error، همراه با مسیر فیلد نامعتبر در پیام

ویژگی‌های کلیدی​

  • ارسال پیام‌های قالب گروهی به ۱ تا ۱۰۰٬۰۰۰ گیرنده در یک درخواست
  • متغیرهای شخصی‌سازی‌شده برای هر گیرنده (HEADER، BODY، BUTTONS، CAROUSEL)
  • defaultPayload مشترک برای همهٔ گیرندگان و ادغام payload اختصاصی هر گیرنده (isMerge)
  • ارسال زمان‌بندی‌شده (scheduleAt)
  • مشخص‌کردن گیرندگان با username (شمارهٔ تلفن)، userId یا sessionId
  • ارسال چند پیام به یک کاربر در یک درخواست گروهی
  • referenceId اختیاری برای پیگیری رکوردها در سیستم‌های بیرونی
  • واگذاری خودکار گفتگوها به یک کاربر یا گروه کاری پس از ارسال (action)
هشدار

این API فقط می‌تواند قالب‌هایی را ارسال کند که توسط Meta تأیید شده‌اند.

نام قالب و کد زبان باید دقیقاً با قالب ثبت‌شده در Meta Business Manager مطابقت داشته باشند.

پارامترهای درخواست​

پارامترهای سطح بالا​

پارامترنوعالزامیتوضیحات
channelUsernamestringبلهشمارهٔ کانال WhatsApp که برای ارسال پیام استفاده می‌شود (مثلاً "908506669933")
connectorBrandstringبلهبرند اتصال‌دهندهٔ کانال. مقدار پشتیبانی‌شدهٔ فعلی: "whatsapp"
titlestringخیرعنوان کمپین. برای نمایش در داشبورد استفاده می‌شود. پیش‌فرض: "-"
scheduleAtstring (ISO 8601)خیرتاریخ و زمان ارسال زمان‌بندی‌شده (مثلاً "2026-06-10T10:00:00.000Z"). اگر ذکر نشود، پیام‌ها بلافاصله ارسال می‌شوند.
defaultPayloadobjectمشروط*payload پیش‌فرض قالب که برای همهٔ گیرندگان اعمال می‌شود. اگر گیرنده‌ای payload اختصاصی خودش را نداشته باشد، الزامی است.
recipientsarrayبلهفهرست گیرندگان. حداقل ۱ و حداکثر ۱۰۰٬۰۰۰ گیرنده.
actionobjectخیراقدامی که پس از ارسال روی گفتگو اعمال می‌شود. assign-to-user یا assign-to-workgroup.

*اگر همهٔ گیرندگان payload اختصاصی خودشان را داشته باشند، defaultPayload اختیاری است.

ساختار گیرنده​

برای هر گیرنده باید دقیقاً یکی از username، userId یا sessionId داده شود. چند شناسه را نمی‌توان هم‌زمان به کار برد و ندادن هیچ‌کدام به خطا منجر می‌شود.

برای ارسال چند پیام به یک کاربر در یک درخواست گروهی، همان شناسه را چند بار به فهرست گیرندگان اضافه کنید.

فیلدنوعتوضیحات
usernamestringشمارهٔ تلفن گیرنده (مثلاً "905554443322"). اگر کاربر در سیستم وجود نداشته باشد، به‌صورت خودکار ساخته می‌شود.
userIdstringشناسهٔ یک کاربر موجود در سیستم.
sessionIdstringشناسهٔ یک نشست گفتگوی موجود در سیستم.
referenceIdstringشناسهٔ مرجع اختیاری (حداکثر ۲۵۶ نویسه) برای تطبیق با رکوردهای سیستم بیرونی (CRM، سفارش، تیکت و غیره). در صورت ذکر، باید در همان درخواست گروهی یکتا باشد.
payloadobjectpayload قالب اختصاصی گیرنده (اختیاری). اگر isMerge: true باشد، با defaultPayload ادغام می‌شود.
isMergebooleanرفتار ادغام را برای این گیرنده بازنویسی می‌کند (اختیاری). اگر ذکر نشود، defaultPayload.isMerge استفاده می‌شود.

ساختار اقدام​

فیلدنوعتوضیحات
action.keystring"assign-to-user" گفتگو را به یک کارشناس مشخص و "assign-to-workgroup" آن را به یک گروه کاری واگذار می‌کند.
action.valuestringشناسهٔ کاربر یا گروه کاری مقصد.

ساختار Payload قالب​

defaultPayload و payload اختصاصی گیرنده ساختار یکسانی دارند. برای پیام‌های قالب باید type: "template" مشخص شود.

{
"type": "template",
"template": {
"templateName": "order_confirmation",
"languageCode": "tr-TR",
"variables": []
},
"isMerge": true
}
فیلدنوعالزامیتوضیحات
typestringبلهنوع payload. برای پیام‌های قالب: "template"
template.templateNamestringبله*نام قالب ثبت‌شده در Meta. وقتی isMerge: true به کار رود، در payload گیرندگان اختیاری است (از payload پیش‌فرض به ارث می‌رسد).
template.languageCodestringخیرکد زبان قالب (مثلاً "tr"، "en"، "tr-TR"، "en-US"). باید دقیقاً با زبان ثبت‌شده در Meta مطابقت داشته باشد.
template.variablesarrayخیرفهرست متغیرهایی که روی اجزای قالب اعمال می‌شوند. جزئیات در ادامه آمده است.
isMergebooleanخیرtrue: payload گیرنده به‌صورت عمیق با defaultPayload ادغام می‌شود. false یا ذکرنشده: payload گیرنده به‌طور کامل جایگزین defaultPayload می‌شود.
رفتار isMerge
  • اگر defaultPayload.isMerge: true باشد و گیرنده payload اختصاصی خودش را بدهد، ادغام عمیق اعمال می‌شود.
  • در ادغام عمیق، گیرنده می‌تواند فقط variables را بدهد؛ templateName و languageCode از payload پیش‌فرض به ارث می‌رسند.
  • فیلد isMerge گیرنده، defaultPayload.isMerge را بازنویسی می‌کند.
  • اگر گیرنده payload ندهد، همیشه defaultPayload استفاده می‌شود.

ساختار متغیرها​

هر مورد از آرایهٔ variables با یک جزء قالب متناظر است. نوع هر جزء (type) فقط یک بار می‌تواند در آرایه بیاید.

typeتوضیحاتparameterscards
"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"]
}

برای قالب‌های کاروسل، متغیرهای اجزا برای هر کارت جداگانه تعریف می‌شوند.

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

پارامترهای درخواست​

پارامترنوعالزامیتوضیحات
bulkOperationIdstringبلهشناسهٔ عملیات گروهی برای پیگیری (برگشتی از 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 قرار دارند.

فیلدنوعتوضیحات
statusstringوضعیت عملیات گروهی: preparing، scheduled، pending، running، stopping، stopped، completed، failed
progress.totalnumberتعداد کل آیتم‌ها
progress.pendingnumberآیتم‌هایی که هنوز در صف منتظرند
progress.succeedednumberآیتم‌هایی که با موفقیت برای ارسال پردازش شده‌اند
progress.failednumberآیتم‌هایی که با خطای ارسال مواجه شده‌اند
progress.cancellednumberآیتم‌هایی که با stop لغو شده‌اند
progress.unknownnumberآیتم‌هایی که نتیجهٔ آن‌ها مشخص نشد
result.outcomestringنتیجهٔ نهایی پس از پایان عملیات گروهی: success، partial، all-failed، cancelled، unknown
result.hasErrorsbooleanاگر دست‌کم یک آیتم ناموفق باشد true است
scheduleAtstringزمان ارسال زمان‌بندی‌شده (فقط برای عملیات‌های گروهی زمان‌بندی‌شده)
startedAt، completedAtstringزمان شروع و پایان ارسال
یادداشت

progress.succeeded نشان می‌دهد که پیام با موفقیت توسط سیستم تحویل داده شده است. این تضمین نمی‌کند که Meta پیام را به گیرنده رسانده باشد. به‌روزرسانی‌های بعدی وضعیت که Meta ایجاد می‌کند (مانند delivered، read یا failed) روی این شمارنده اثری ندارند و اینجا منعکس نمی‌شوند.

Show در برابر Message Stats
  • اندپوینت 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

پارامترهای درخواست​

پارامترنوعالزامیتوضیحات
bulkOperationIdstringبلهشناسهٔ عملیات گروهی برای پرس‌وجو
{
"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​

فیلدنوعتوضیحات
pendingnumberپیام‌هایی که هنوز ارسال نشده‌اند
sendingnumberپیام‌هایی که در حال ارسال‌اند
sentnumberپیام‌هایی که به WhatsApp تحویل شده‌اند اما هنوز به مقصد نرسیده‌اند
deliverednumberپیام‌هایی که به گوشی گیرنده رسیده‌اند
readnumberپیام‌هایی که گیرنده خوانده است
deletednumberپیام‌های حذف‌شده
warningnumberپیام‌های دارای هشدار
errornumberپیام‌های دارای خطا
unknownnumberپیام‌هایی با وضعیت نامشخص
not-sendednumberپیام‌هایی که ارسال نشدند
no-messagenumberآیتم‌های بدون پیام (هنوز در صف، ناموفق یا لغوشده)
totalnumberتعداد کل آیتم‌ها
نکته‌های استفاده
  • عدد بالای no-message در حالی که عملیات گروهی running است، نشان می‌دهد ارسال هنوز در جریان است
  • عدد delivered تحویل موفق پیام‌ها را نشان می‌دهد
  • از عدد read می‌توان برای سنجش میزان تعامل با کمپین استفاده کرد
  • برای پیگیری لحظه‌ای می‌توانید این اندپوینت را به‌صورت دوره‌ای فراخوانی کنید

ساختار Options — صفحه‌بندی، فیلتر و مرتب‌سازی​

پارامتر options که در اندپوینت‌های list و list-with-messages استفاده می‌شود، ساختاری استاندارد برای صفحه‌بندی، فیلتر و مرتب‌سازی فراهم می‌کند.

فیلدهای Options​

فیلدنوعالزامیتوضیحات
paginationobjectخیرتنظیمات صفحه‌بندی
filteringobjectخیرمعیارهای فیلتر
sortingobjectخیرتنظیمات مرتب‌سازی (مثلاً {"position": 1})

ساختار صفحه‌بندی​

اگر pagination ارسال نشود، صفحهٔ اول با حداکثر ۵۰ آیتم برگردانده می‌شود. اگر ارسال شود، currentPage الزامی است.

فیلدنوعپیش‌فرضتوضیحات
currentPagenumber—شمارهٔ صفحه‌ای که باید دریافت شود (هنگام ارسال pagination الزامی است)
pageItemsnumber50تعداد آیتم در هر صفحه (حداکثر: ۵۰)

ساختار فیلتر​

فیلتر با قالب کوئری 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

پارامترهای درخواست​

پارامترنوعالزامیتوضیحات
bulkOperationIdstringبلهشناسهٔ عملیات گروهی برای فهرست‌کردن
optionsobjectخیرگزینه‌های صفحه‌بندی، فیلتر و مرتب‌سازی. برای جزئیات ساختار 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": {}
}
}

فیلدهای آیتم​

فیلدنوعتوضیحات
_idstringشناسهٔ آیتم
bulkOperationIdstringشناسهٔ عملیات گروهی‌ای که این آیتم به آن تعلق دارد
positionnumberترتیب گیرنده در آرایهٔ recipients درخواست، از 0
payloadobjectگیرنده دقیقاً همان‌طور که در درخواست ارسال شده (username / userId / sessionId، referenceId، payload، isMerge)
statusstringوضعیت آیتم: pending، succeeded، failed، cancelled، unknown
errorobjectفقط در آیتم‌های failed: code، message و retryable
result.userIdstringشناسهٔ کاربر گیرنده
result.sessionIdstringشناسهٔ نشست گفتگو
result.messageIdstringشناسهٔ پیام ارسال‌شده (اگر پیامی ارسال نشده باشد وجود ندارد)
completedAtstringزمان پردازش آیتم
messageobjectجزئیات پیام (فقط در list-with-messages)
sessionobjectجزئیات گفتگو (فقط در list-with-messages)

مقایسهٔ List و List-With-Messages​

ویژگیlistlist-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
هشدار

این اقدام قابل بازگشت نیست. ارسال متوقف‌شده را نمی‌توان از سر گرفت.

پارامترهای درخواست​

پارامترنوعالزامیتوضیحات
bulkOperationIdstringبلهشناسهٔ عملیات گروهی برای توقف
{
"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.