같은 템플릿을 수신자마다 다른 값으로 보낼 때 써요. 수신자별로 변수와 ref_id 를 따로 줄 수 있어요.
100명을 넣으면 100건이 나가요. 테스트할 때 목록을 그대로 넣지 않도록 주의해요.
실패 처리 규칙
| 상황 | 동작 |
|---|---|
| 쿼터 초과 | 요청 시점에 전체 거부 (3022). 일부만 나가지 않아요. 한도는 recipients 길이 전체로 세요 — 제외 목록으로 건너뛸 건·이미 접수된 ref_id 도 포함돼요. 새 프로젝트는 분당 10건이 상한이라 11명부터 거부돼요(개요) |
| 개별 수신자 변수 누락·번호 오류 | 그 건만 rejected, 나머지는 정상 발송 |
| 발송 제외 목록에 있는 번호 | 그 건은 skipped. 과금되지 않고 발송 기록도 만들지 않아요 |
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
template_code |
String | 필수 | 템플릿 코드 |
recipients |
Array | 필수 | 수신자 배열 (최소 1건) |
└─ to |
String | 필수 | 수신번호 |
└─ variables |
Object | 선택 | 이 수신자의 변수 치환값 |
└─ ref_id |
String | 선택 | 이 수신자의 멱등 키 |
reserved_at |
String | 선택 | 예약 발송 시각 (ISO8601) |
sender_key |
String | 선택 | 발신 채널 senderKey |
user_id |
String | 선택 | 가맹점 회원 식별자 (기록용) |
webhook_url |
String | 선택 | 이 요청으로 나간 모든 건의 결과 웹훅을 받을 주소 (https:// 만, 2,000자 이하) |
수신자마다 따로 정할 수는 없고, 이 요청으로 나간 모든 수신자 건의 결과 웹훅이 같은 주소로 가요. 형식이 틀리면 한 건도 나가지 않고 요청 전체가 3028 로 거부돼요. 자세한 규칙은 단건 발송과 같아요.
지금은 reserved_at 이 날짜로 해석되지 않거나 과거 시각이면 오류 없이 모든 수신자에게 바로 보내요(과금 포함). 예약할 때는 2026-08-27T10:00:00+09:00 처럼 오프셋까지 붙인 ISO8601 로 보내고, 응답의 receipt_id 로 발송 결과 조회를 불러 reserved_at 이 채워졌는지 확인해요.
쿼리스트링으로 보낸 JSON 문자열도 받아 주지만 URL 길이 제한이 있어 권장하지 않아요.
코드 예제
curl -X POST "https://message.bootapi.com/alimtalk/send/bulk" \
-H "Authorization: Basic {base64(client_key:secret_key)}" \
-H "Content-Type: application/json" \
-d '{
"template_code": "G_RESTOCK_NOTICE_user",
"recipients": [
{
"to": "01012345678",
"ref_id": "bulk-0001",
"variables": { "company_name": "부트페이몰", "user_name": "홍길동" }
},
{
"to": "01087654321",
"ref_id": "bulk-0002",
"variables": { "company_name": "부트페이몰", "user_name": "김철수" }
}
]
}'bashrequire 'bootpay'
commerce = BootpayStore::RestClient.new(client_key: 'your-commerce-client-key', secret_key: 'your-commerce-secret-key')
response = commerce.alimtalk_send_bulk(
template_code: 'G_RESTOCK_NOTICE_user',
recipients: [
{ to: '01012345678', ref_id: 'bulk-0001', variables: { company_name: '부트페이몰', user_name: '홍길동' } },
{ to: '01087654321', ref_id: 'bulk-0002', variables: { company_name: '부트페이몰', user_name: '김철수' } }
]
)
puts response.dataruby응답
{
"count": 2,
"requested": 2,
"skipped": 0,
"rejected": 0,
"receipts": [
{
"receipt_id": "68b0f2a1c3d4e5f6a7b8c9d0",
"ref_id": "bulk-0001",
"to": "01012345678",
"status": "requested"
},
{
"receipt_id": "68b0f2a1c3d4e5f6a7b8c9d1",
"ref_id": "bulk-0002",
"to": "01087654321",
"status": "requested"
}
]
}json| 필드 | 설명 |
|---|---|
| count | 요청한 수신자 수 |
| requested | 접수에 성공한 건수 |
| skipped | 발송 제외 목록에 걸려 건너뛴 건수 |
| rejected | 건별로 거부된 건수 |
같은 ref_id 로 이미 접수된 건은 기존 결과를 그대로 돌려줘서 status 가 success·canceled 일 수 있어요. 이런 건은 receipts 에만 있고 requested·skipped·rejected 어디에도 세지 않아서, 세 값의 합이 count 보다 작을 수 있어요.
거부·건너뜀 항목에는 사유 code 가 함께 들어와요. 에러 응답의 error_code 와 달리 이 code 는 숫자예요.
{
"to": "01099998888",
"status": "skipped",
"code": 3021
}json발송 전에 제외 번호를 걸러요
벌크에서 skipped 로 빠지는 건이 많다면 발송 전 확인으로 미리 정리하는 편이 좋아요. 한 번에 최대 1,000건까지 확인할 수 있어요.
에러 코드
| 코드 | error_code | 메시지 | 대처 방법 |
|---|---|---|---|
-48 |
INVALID_PARAMETER |
recipients 형식 오류 | JSON 배열로 보냈는지, to 가 있는지 확인해요 |
3022 |
RATE_LIMIT_EXCEEDED |
발송 한도 초과 | 요청 전체가 거부됐어요. 건수를 나눠 보내요 |
3028 |
WEBHOOK_URL_INVALID |
웹훅 주소 형식 오류 | 요청 전체가 거부됐어요. webhook_url 은 https:// 로 시작하고 2,000자 이하여야 해요 |
11101 |
MESSAGE_PREPAID_NOT_CHARGED |
선불 첫 충전 전 | 요청 전체가 거부됐어요. 첫 충전을 마친 뒤 보내요 |
16312 |
SERVICE_CHARGE_BALANCE_INSUFFICIENT |
선불 잔액 부족 | 잔액이 모자란 건만 rejected 로 빠지고 code 에 담겨요. 충전 후 그 건만 다시 보내요 |
그 밖의 게이트 오류는 단건 발송과 같아요.
