본문으로 건너뛰기

Bulk Template Message API 문서

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

메시지 발송은 비동기로 처리됩니다. API 요청은 즉시 bulkMessageId를 반환하고, 메시지는 대기열에 들어가 백그라운드에서 처리됩니다.

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

주요 기능​

  • 한 번의 요청으로 1~5,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명, 최대 5,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와 깊은 병합(deep merge)됩니다. 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/bulk-message/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시는 07:00:00.000Z)
  • 이 예시는 이름 있는 파라미터 형식을 사용하므로, 템플릿에 {{name}}, {{discountRate}} 같은 자리 표시자가 있어야 합니다
  • action을 지정하면 메시지 발송으로 생성된 대화가 지정한 워크그룹에 자동 배정됩니다

응답​

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

{
"bulkMessageId": "abc123xyz789"
}

오류 응답​

상태설명
Channel not foundchannelUsername, connectorBrand와 일치하는 인증된 채널을 찾을 수 없습니다
Channel is not verified일치하는 채널은 있지만 아직 인증되지 않았습니다
invalidRequest수신자에 여러 식별자를 지정했거나 필수 페이로드가 누락되었습니다
channelNotFound입력한 channelId와 일치하는 활성 채널을 찾을 수 없습니다
schema-error요청 본문의 필드가 스키마 검증에 실패했습니다(필수 필드 누락, 잘못된 유형 또는 지원하지 않는 값)

반환된 bulkMessageId는 아래 엔드포인트에서 발송 통계를 조회하거나 발송을 중단하는 데 사용할 수 있습니다.


Stats — 발송 통계​

대량 메시지의 현재 발송 상태를 조회합니다. 대기 중, 성공, 실패, 취소된 레코드 수를 반환합니다.

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

요청 파라미터​

파라미터유형필수설명
bulkMessageIdstring예추적할 대량 메시지의 ID(createBulkPerRecipient에서 반환됨)
{
"bulkMessageId": "abc123xyz789"
}
curl --location 'https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/bulk-message/stats.js' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN' \
--data '{
"bulkMessageId": "abc123xyz789"
}'

응답​

{
"bulkMessage": {
"_id": "abc123xyz789",
"slug": "my-workspace",
"title": "Campaign Title",
"channelUsername": "908506669933",
"connectorBrand": "whatsapp",
"connectorSlug": "whatsapp-cloud",
"payload": {
"recipientCount": 100,
"defaultPayloadType": "template",
"jobId": "job_abc123"
},
"schedule": {
"date": "2026-06-10T07:00:00.000Z",
"type": "later"
},
"createdAt": "2026-06-08T10:00:00.000Z",
"updatedAt": "2026-06-08T10:30:00.000Z"
},
"stats": {
"pending": 45,
"success": 42,
"failed": 3,
"cancelled": 10,
"total": 100
}
}

Stats 필드​

필드유형설명
stats.pendingnumber아직 대기열에서 기다리는 레코드 수
stats.successnumber발송 처리에 성공한 레코드 수
stats.failednumber발송 오류가 발생한 레코드 수
stats.cancellednumber취소된 레코드 수
stats.totalnumber전체 레코드 수(pending + success + failed + cancelled)
노트

stats.success는 시스템이 메시지를 성공적으로 전달 처리했다는 뜻입니다. Meta가 메시지를 수신자에게 전달했음을 보장하는 것은 아닙니다. Meta가 이후에 생성하는 상태 업데이트(delivered, read, failed 등)는 이 카운터에 영향을 주지 않으며 여기에 반영되지 않습니다.

Stats와 Message Stats 비교
  • stats 엔드포인트: 발송 작업의 상태(pending, success, failed, cancelled)를 보여 줍니다
  • message-stats 엔드포인트: 실제 메시지 상태(pending, sending, sent, delivered, read, error 등)를 보여 줍니다
  • 작업은 성공했더라도 메시지가 아직 전달되지 않았거나 읽히지 않았을 수 있습니다

참조 ID​

referenceId는 추적을 위해 발송 레코드를 자체 시스템의 ID(예: 주문 번호, 티켓 ID)와 대조할 때 사용하는 선택 필드입니다.

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

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

Message Stats — WhatsApp 메시지 상태​

대량 메시지의 메시지 상태 통계를 조회합니다. stats 엔드포인트와 달리 작업 상태가 아니라 레코드별 실제 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/bulk-message/message-stats.js

요청 파라미터​

파라미터유형필수설명
bulkMessageIdstring예조회할 대량 메시지의 ID
{
"bulkMessageId": "abc123xyz789"
}
curl --location 'https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/bulk-message/message-stats.js' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN' \
--data '{
"bulkMessageId": "abc123xyz789"
}'

응답​

{
"bulkMessage": {
"_id": "abc123xyz789",
"slug": "my-workspace",
"type": "perRecipient",
"title": "Campaign Title"
},
"messageStats": {
"pending": 5,
"sending": 2,
"sent": 10,
"delivered": 45,
"read": 30,
"deleted": 2,
"warning": 1,
"error": 3,
"unknown": 0,
"not-sended": 2,
"no-message": 10,
"total": 120
}
}

Message Stats 필드​

필드유형설명
pendingnumber아직 발송되지 않은 메시지
sendingnumber현재 발송 중인 메시지
sentnumberWhatsApp에 전달되었으나 아직 수신되지 않은 메시지
deliverednumber수신자의 휴대폰에 전달된 메시지
readnumber수신자가 읽은 메시지
deletednumber삭제된 메시지
warningnumber경고가 있는 메시지
errornumber오류가 있는 메시지
unknownnumber상태를 알 수 없는 메시지
not-sendednumber발송하지 못한 메시지
no-messagenumber아직 메시지가 없는 레코드(대기열에 남아 있음)
totalnumber전체 레코드 수
사용 팁
  • no-message 수가 많으면 발송이 아직 진행 중이라는 뜻입니다
  • delivered 수는 메시지가 성공적으로 전달된 건수를 보여 줍니다
  • read 수로 캠페인 참여도를 측정할 수 있습니다
  • 이 엔드포인트를 주기적으로 호출해 실시간으로 추적할 수 있습니다

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

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

Options 필드​

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

Pagination 구조​

필드유형기본값설명
currentPagenumber1조회할 페이지 번호
pageItemsnumber50페이지당 레코드 수(최대 50)

Filtering 구조​

필터링은 MongoDB 쿼리 형식을 사용합니다. 레코드 필드로 필터링할 수 있으며, list-with-messages 엔드포인트에서는 메시지 필드(예: message.status)로도 필터링할 수 있습니다.

// Filter by record status
{
"options": {
"filtering": {
"status": "success"
}
}
}

// Filter by referenceId
{
"options": {
"filtering": {
"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"]
}
}
}
}

Sorting 구조​

정렬은 MongoDB 정렬 형식을 사용합니다. 1은 오름차순, -1은 내림차순입니다.

// Sort by creation date descending (newest first)
{
"options": {
"sorting": {
"createdAt": -1
}
}
}

// Sort by multiple fields
{
"options": {
"sorting": {
"status": 1,
"createdAt": -1
}
}
}

조합 사용 예시​

{
"bulkMessageId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 2,
"pageItems": 50
},
"filtering": {
"message.status": "delivered"
},
"sorting": {
"createdAt": -1
}
}
}

이 예시는 전달된 메시지를 최신순으로 정렬합니다.

실용 팁
  • filtering과 pagination을 함께 사용해 원하는 레코드만 조회하세요
  • 응답에는 페이지 이동에 쓸 수 있는 options.pagination.totalCount와 options.pagination.totalPages가 포함됩니다
  • message.status 필터는 list-with-messages 엔드포인트에서만 작동합니다

Records — 레코드 상세 및 목록​

대량 메시지의 모든 발송 레코드를 조회합니다. 각 레코드는 수신자에게 보낸 메시지 하나를 나타냅니다.

List — 레코드 목록​

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

요청 파라미터​

파라미터유형필수설명
bulkMessageIdstring예조회할 대량 메시지의 ID
optionsobject아니요페이지 나누기, 필터링, 정렬 옵션. 자세한 내용은 Options 구조를 참고하세요.

요청 — 기본 사용법​

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

요청 — 특정 상태로 필터링​

{
"bulkMessageId": "abc123xyz789",
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50
},
"filtering": {
"status": "success"
}
}
}

발송에 성공한 레코드(record.status = "success")만 조회합니다.

응답​

{
"bulkMessageRecords": [
{
"_id": "record_1",
"bulkMessageId": "abc123xyz789",
"slug": "my-workspace",
"userId": "user_123",
"sessionId": "session_456",
"messageId": "msg_789",
"referenceId": "ORDER-12345",
"status": "success",
"createdAt": "2026-06-10T10:00:00.000Z"
}
],
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50,
"totalCount": 150,
"totalPages": 3
},
"sorting": {},
"filtering": {}
}
}

List With Messages — 메시지 상세가 포함된 목록​

list 엔드포인트와 기능(페이지 나누기, 필터링, 정렬)이 같습니다. 단 하나의 차이점은 레코드에 messageId가 있으면 관련 message 객체도 함께 반환된다는 것입니다. 한 번의 조회로 메시지 상태(delivered, read, error 등)와 페이로드를 확인할 수 있습니다.

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

요청 — 기본 사용법​

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

요청 — 메시지 상태로 필터링​

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

이 예시는 "delivered" 상태인 메시지만 가져옵니다.

요청 — 실패한 메시지 조회​

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

발송 오류가 발생한 메시지를 확인할 때 사용합니다.

응답​

{
"bulkMessageRecords": [
{
"_id": "record_1",
"bulkMessageId": "abc123xyz789",
"userId": "user_123",
"messageId": "msg_789",
"referenceId": "ORDER-12345",
"status": "success",
"message": {
"_id": "msg_789",
"status": "delivered",
"payload": {
"type": "template",
"template": {
"templateName": "order_notification"
}
},
"createdAt": "2026-06-10T10:01:00.000Z",
"deliveredAt": "2026-06-10T10:01:05.000Z"
}
}
],
"options": {
"pagination": {
"currentPage": 1,
"pageItems": 50,
"totalCount": 150,
"totalPages": 3
},
"sorting": {},
"filtering": {}
}
}

Record 필드​

필드유형설명
_idstring레코드 ID
bulkMessageIdstring이 레코드가 속한 대량 메시지 ID
userIdstring수신자 사용자 ID
sessionIdstring대화 세션 ID
messageIdstring발송된 메시지 ID(아직 발송되지 않았으면 null)
referenceIdstring선택 참조 ID(입력한 경우)
statusstring레코드 상태: pending, success, failed, cancelled
messageobject메시지 상세(list-with-messages에서만)

List와 List-With-Messages 비교​

기능listlist-with-messages
페이지 나누기예예
필터링예(레코드 필드만)예(레코드 + 메시지 필드)
Message 객체아니요예(messageId가 있는 경우)
성능빠름보통(JOIN 수행)
message.status 필터사용 불가사용 가능
사용 시나리오
  • list: 모든 레코드를 빠르게 조회하고, referenceId로 필터링하고, 레코드 상태를 확인
  • list-with-messages: WhatsApp 메시지 상태(delivered, read)를 확인하고 메시지 페이로드를 검토
  • 필터링 예시:
  • {"status": "failed"} — 발송에 실패한 레코드
  • {"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 전용)
  • {"status": {"$in": ["success", "pending"]}} — 성공했거나 대기 중인 레코드
성능 및 사용 참고 사항
  • list 엔드포인트가 더 빠르므로 일반적인 상태 확인에는 이것을 사용하세요
  • 메시지 상세가 필요하면 list-with-messages를 사용하세요
  • 페이지당 최대 레코드 수(pageItems)는 50이며, 지정하지 않으면 기본값은 50입니다
  • message.status 필터는 list-with-messages에서만 작동합니다
  • messageId가 null인 레코드는 message 객체도 null입니다

Stop — 발송 취소​

진행 중인 대량 메시지 발송을 중단합니다. 대기 중인 작업이 취소되고 모든 pending 레코드가 cancelled 상태로 바뀝니다. 이미 success 또는 failed로 처리된 레코드는 영향을 받지 않습니다.

POST https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/bulk-message/stop.js
경고

이 작업은 되돌릴 수 없습니다. 중단된 발송은 다시 시작할 수 없습니다.

요청 파라미터​

파라미터유형필수설명
bulkMessageIdstring예중단할 대량 메시지의 ID
{
"bulkMessageId": "abc123xyz789"
}
curl --location 'https://app.monochat.ai/api/:slug/custom-functions/bulk-message-api-app/api/bulk-message/stop.js' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN' \
--data '{
"bulkMessageId": "abc123xyz789"
}'

응답​

요청이 성공하면 응답 본문은 비어 있습니다. HTTP 200 상태는 작업이 성공적으로 완료되었음을 나타냅니다.

{}
팁
  • 이 API로 생성한 대량 메시지만 중단할 수 있습니다
  • 발송을 중단한 후 stats 엔드포인트로 취소된 레코드 수를 확인할 수 있습니다
  • 대량 메시지를 찾을 수 없으면 API가 bulkMessageNotFound 오류를 반환합니다