본문으로 건너뛰기

Bulk Template Message API 문서 — v2

이 API를 사용하면 WhatsApp 채널을 통해 여러 수신자에게 템플릿 메시지를 동시에 보낼 수 있습니다.

메시지 발송은 비동기로 처리됩니다. 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 요청 본문은 바뀌지 않으며, 이제 한 번의 요청에 5,000명이 아니라 최대 100,000명의 수신자를 담을 수 있습니다.
  2. 요청에서 bulkMessageId 대신 bulkOperationId를 보냅니다.
  3. stats.js를 show.js로 바꿉니다.
  4. 아래 표의 새 필드 이름으로 응답을 읽습니다.

v2 엔드포인트는 v2 send로 생성된 대량 작업만 인식합니다. 전환하기 전에 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.jslist와 같으며, 각 항목에 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(항목)
요청, send 응답, 레코드의 bulkMessageIdbulkOperationId
보낸 그대로의 레코드 username / userId / sessionIdpayload.username / payload.userId / payload.sessionId
레코드 referenceIdpayload.referenceId
레코드 payload(수신자의 템플릿 페이로드)payload.payload
발송 후 레코드의 userId, sessionId, messageIdresult.userId, result.sessionId, result.messageId
레코드 상태 successsucceeded
실패한 레코드의 errorMessageerror.message(error.code, error.retryable과 함께)
—position(recipients에서 수신자의 인덱스)
필터 {"status": "success"}{"status": "succeeded"}
필터 {"referenceId": "…"}{"payload.referenceId": "…"}
send 오류 schema-error, invalidRequestzod-schema-error(메시지에 잘못된 필드의 경로 포함)

주요 기능​

  • 한 번의 요청으로 1~100,000명의 수신자에게 대량 템플릿 메시지 발송
  • 수신자별 개인화 변수(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를 제공하지 않으면 필수입니다.
recipientsarray예수신자 목록. 최소 1명, 최대 100,000명.
actionobject아니요발송 후 대화에 적용할 동작. assign-to-user 또는 assign-to-workgroup.

*모든 수신자가 자체 payload를 제공하면 defaultPayload는 선택 사항입니다.

수신자 구조​

수신자마다 username, userId, sessionId 중 정확히 하나를 입력해야 합니다. 여러 식별자를 함께 사용할 수 없으며, 모두 생략하면 오류가 발생합니다.

하나의 대량 요청 안에서 같은 사용자에게 여러 메시지를 보내려면 같은 식별자를 수신자 목록에 여러 번 추가하세요.

필드유형설명
usernamestring수신자의 전화번호(예: "905554443322"). 시스템에 없는 사용자는 자동으로 생성됩니다.
userIdstring시스템에 있는 기존 사용자의 ID.
sessionIdstring시스템에 있는 기존 대화 세션의 ID.
referenceIdstring외부 시스템 레코드(CRM, 주문, 티켓 등)와 대조하기 위한 선택 참조 ID(최대 256자). 입력하는 경우 대량 요청 안에서 고유해야 합니다.
payloadobject수신자별 템플릿 페이로드(선택). isMerge: true이면 defaultPayload와 병합됩니다.
isMergeboolean이 수신자에 대한 병합 동작을 재정의합니다(선택). 생략하면 defaultPayload.isMerge가 사용됩니다.

Action 구조​

필드유형설명
action.keystring"assign-to-user"는 대화를 특정 상담원에게, "assign-to-workgroup"은 워크그룹에 배정합니다.
action.valuestring대상 사용자 또는 워크그룹의 ID.

템플릿 페이로드 구조​

defaultPayload와 수신자별 payload는 같은 구조를 사용합니다. 템플릿 메시지에는 type: "template"을 지정해야 합니다.

{
"type": "template",
"template": {
"templateName": "order_confirmation",
"languageCode": "tr-TR",
"variables": []
},
"isMerge": true
}
필드유형필수설명
typestring예페이로드 유형. 템플릿 메시지의 경우: "template"
template.templateNamestring예*Meta에 등록된 템플릿 이름. isMerge: true를 사용하면 수신자 페이로드에서는 선택 사항입니다(기본값에서 상속).
template.languageCodestring아니요템플릿 언어 코드(예: "tr", "en", "tr-TR", "en-US"). Meta에 등록된 언어와 정확히 일치해야 합니다.
template.variablesarray아니요템플릿 구성 요소에 적용할 변수 목록. 자세한 내용은 아래를 참고하세요.
isMergeboolean아니요true: 수신자 페이로드가 defaultPayload와 깊은 병합됩니다. false 또는 생략: 수신자 페이로드가 defaultPayload를 완전히 대체합니다.
isMerge 동작
  • defaultPayload.isMerge: true이고 수신자가 자체 payload를 제공하면 깊은 병합이 적용됩니다.
  • 깊은 병합에서는 수신자가 variables만 제공해도 되며, templateName과 languageCode는 기본 페이로드에서 상속됩니다.
  • 수신자의 isMerge 필드가 defaultPayload.isMerge보다 우선합니다.
  • 수신자가 payload를 제공하지 않으면 항상 defaultPayload가 사용됩니다.

변수 구조​

variables 배열의 각 항목은 템플릿 구성 요소 하나에 해당합니다. 하나의 구성 요소 유형(type)은 배열에 한 번만 나타날 수 있습니다.

type설명parameterscards
"HEADER"템플릿 헤더 구성 요소. 텍스트 변수 또는 미디어 URL(이미지, 동영상, 문서)예—
"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로 수신자별 변수 지정​

수신자마다 다른 변수 값을 사용합니다. defaultPayload에 isMerge: true를 설정하면 수신자는 variables만 제공하면 되고, templateName과 languageCode는 기본 페이로드에서 상속됩니다.

이 예시의 템플릿에는 미디어 헤더와 개인화된 본문 변수가 모두 포함되어 있습니다.

{
"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을 반복할 필요가 없습니다
  • 각 수신자의 variables 배열은 완전히 독립적입니다
  • 헤더 미디어 URL은 공개적으로 접근할 수 있어야 합니다
  • 수신자 페이로드에서 type을 생략하면 defaultPayload.type이 상속됩니다

4. URL 버튼 변수​

템플릿에 동적 URL 접미사가 있는 버튼이 있을 때 BUTTONS 구성 요소를 사용합니다. 예를 들어 기본 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"]
}
]
}
}
}
]
}
팁
  • BUTTONS 파라미터는 변수가 있는 버튼의 순서에 따라 대응됩니다
  • FLOW 버튼과 변수가 없는 버튼은 무시되며, 인덱스는 동적 URL 버튼에만 적용됩니다
  • 템플릿 버튼 URL이 https://example.com/track/{{1}}이면 parameters[0]이 {{1}}을 대체합니다

5. 예약 발송​

미래의 날짜와 시간에 메시지를 보내도록 예약합니다. scheduleAt은 ISO 8601 UTC 타임스탬프로 입력해야 합니다.

{
"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 시간대로 입력해야 합니다(예: 튀르키예 시간 오전 10:00이면 07:00:00.000Z 사용)
  • 이 예시는 이름 있는 파라미터 형식을 사용하므로 템플릿에 {{name}}, {{discountRate}} 같은 자리표시자가 있어야 합니다
  • action을 사용하면 메시지 발송으로 생성된 대화가 지정한 워크그룹에 자동으로 배정됩니다

응답​

요청이 성공하면 발송 진행 상황을 추적하는 데 사용할 수 있는 bulkOperationId가 반환됩니다.

{
"bulkOperationId": "abc123xyz789"
}

오류 응답​

상태설명
recipients field is requiredrecipients 필드를 보내지 않았습니다
Channel not foundchannelUsername, connectorBrand와 일치하는 인증된 채널을 찾을 수 없습니다
Channel is not verified일치하는 채널은 있지만 아직 인증되지 않았습니다
zod-schema-error요청 본문의 필드가 스키마 검증에 실패했습니다(필수 필드 누락, 잘못된 유형, 지원하지 않는 값, 수신자 한 명에 둘 이상의 식별자, 또는 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예추적할 대량 작업의 ID(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.cancellednumberstop으로 취소된 항목 수
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여도 메시지가 아직 전달되지 않았거나 읽히지 않았을 수 있습니다

참조 ID​

referenceId는 추적을 위해 발송 항목을 자체 시스템의 ID(예: 주문 번호, 티켓 ID)와 대조할 때 사용하는 선택 필드입니다. 각 항목에서 payload.referenceId로 반환됩니다.

입력하는 경우 대량 요청 안에서 고유해야 합니다(최대 256자).

중요
  • 반드시 사용해야 하는 것은 아닙니다
  • 같은 대량 작업 안에서는 중복될 수 없습니다
  • 서로 다른 대량 작업에서는 다시 사용할 수 있습니다

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예조회할 대량 작업의 ID
{
"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현재 발송 중인 메시지
sentnumberWhatsApp에 넘겨졌지만 아직 전달되지 않은 메시지
deliverednumber수신자의 휴대전화에 전달된 메시지
readnumber수신자가 읽은 메시지
deletednumber삭제된 메시지
warningnumber경고가 있는 메시지
errornumber오류가 있는 메시지
unknownnumber상태를 알 수 없는 메시지
not-sendednumber발송할 수 없었던 메시지
no-messagenumber메시지가 없는 항목(아직 대기 중이거나 실패 또는 취소됨)
totalnumber전체 항목 수
사용 팁
  • 대량 작업이 running인 동안 no-message 수가 많으면 발송이 아직 진행 중이라는 뜻입니다
  • delivered 수는 메시지가 성공적으로 전달된 건수를 보여 줍니다
  • read 수로 캠페인 참여도를 측정할 수 있습니다
  • 이 엔드포인트를 주기적으로 호출해 실시간으로 추적할 수 있습니다

Options 구조 — 페이지 나누기, 필터링, 정렬​

list와 list-with-messages 엔드포인트에서 사용하는 options 파라미터는 페이지 나누기, 필터링, 정렬을 위한 표준 구조를 제공합니다.

Options 필드​

필드유형필수설명
paginationobject아니요페이지 나누기 설정
filteringobject아니요필터 조건
sortingobject아니요정렬 설정(예: {"position": 1})

Pagination 구조​

pagination을 보내지 않으면 첫 페이지가 최대 50개 항목으로 반환됩니다. 보내는 경우 currentPage는 필수입니다.

필드유형기본값설명
currentPagenumber—조회할 페이지 번호(pagination을 보낼 때 필수)
pageItemsnumber50페이지당 항목 수(최대 50)

Filtering 구조​

필터링은 항목 필드에 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
}
}
}
}

Sorting 구조​

정렬은 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예나열할 대량 작업의 ID
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 등)와 페이로드를 한 번의 쿼리로 확인할 수 있습니다.

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항목 ID
bulkOperationIdstring이 항목이 속한 대량 작업 ID
positionnumber요청의 recipients 배열에서 수신자의 순서(0부터 시작)
payloadobject요청에서 보낸 그대로의 수신자(username / userId / sessionId, referenceId, payload, isMerge)
statusstring항목 상태: pending, succeeded, failed, cancelled, unknown
errorobjectfailed 항목에만 있음: code, message, retryable
result.userIdstring수신자 사용자 ID
result.sessionIdstring대화 세션 ID
result.messageIdstring발송된 메시지 ID(메시지가 발송되지 않았으면 없음)
completedAtstring항목이 처리된 시간
messageobject메시지 상세(list-with-messages에만 있음)
sessionobject대화 상세(list-with-messages에만 있음)

List와 List-With-Messages 비교​

기능listlist-with-messages
페이지 나누기예예
필터링예(항목 필드만)예(항목 + 메시지 필드)
Message / Session아니요예(있는 경우)
성능빠름보통(JOIN 수행)
message.status 필터사용 불가사용 가능
사용 시나리오
  • list: 모든 항목을 빠르게 나열, referenceId로 필터링, 항목 상태 확인
  • list-with-messages: WhatsApp 메시지 상태(delivered, read) 확인, 메시지 페이로드 검토
  • 필터링 예시:
    • {"status": "failed"} — 발송에 실패한 항목
    • {"payload.referenceId": "ORDER-123"} — 특정 참조 ID
    • {"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)는 50이며, 지정하지 않으면 기본값은 50입니다
  • 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예중단할 대량 작업의 ID
{
"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.