결과 조회 · 예약 취소
접수한 발송 건의 진행 상태와 수신자별 최종 결과를 조회하고, 아직 발송되지 않은 예약 건을 취소합니다. 결과를 실시간으로 받고 싶다면 Webhook 을 함께 사용하세요.
조회·취소 대상은 API 로 접수한 건뿐입니다. 같은 계정이 웹사이트에서 발송한 건은 이 API 로 조회·취소할 수 없고 404 MESSAGE_NOT_FOUND 로 응답하며, 목록에도 나타나지 않습니다. (웹사이트의 발송 이력에서는 API 로 접수한 건에 API 표시가 붙습니다.)
상태 값
발송 건(message) 상태
| status | 의미 |
|---|---|
ACCEPTED | 접수됨, 발송 대기/진행 전 |
SCHEDULED | 예약 접수됨 (발송 시각 전) |
PROCESSING | 발송 처리 중 |
PARTIAL | 일부 수신자만 처리 완료 |
COMPLETED | 모든 수신자의 결과가 확정됨 |
CANCELLED | 취소됨 |
수신자(recipient) 상태
| status | 의미 | 최종? |
|---|---|---|
PENDING | 발송 대기 (예약 포함) | 아니오 |
SENDING | 발송 처리 중 | 아니오 |
SUBMITTED | 통신사/카카오에 전달됨, 도달 결과 대기 | 아니오 |
DELIVERED | 수신 확인(성공) | 예 |
FAILED | 실패 — 요금 자동 환불 | 예 |
REJECTED | 발송 대행 시스템이 접수 자체를 거절 — 요금 자동 환불 | 예 |
CANCELLED | 예약 취소됨 — 요금 환불 | 예 |
통신사 사정에 따라 SUBMITTED 에서 최종 결과가 확정되기까지 시간이 걸릴 수 있습니다. (대부분 수 초 이내) 최종 상태가 되기 전까지는 폴링하거나 Webhook 을 기다리세요.
발송 건 상세
GET /v1/messages/{message_id}
JSON
{
"request_id": "req_b5e9af0145c737cec045",
"data": {
"message_id": 1375,
"channel": "sms",
"status": "COMPLETED",
"name": "11/06 21:35 - 출고 안내",
"from": "0212345678",
"client_ref": "ship-20261107-am",
"request_id": "req_e8cfaea0a9c54b8a3e10",
"send_at": "2026-11-07T09:00:00+09:00",
"counts": { "total": 2, "delivered": 1, "failed": 1, "pending": 0 },
"amount": 10.5,
"created_at": "2026-11-06T21:35:51+09:00"
}
}counts.pending 이 0 이 되면 모든 결과가 확정된 것입니다. amount 는 환불을 반영한 실제 청구 금액입니다.
수신자별 결과
GET /v1/messages/{message_id}/recipients
| 쿼리 | 설명 |
|---|---|
status | 수신자 상태 필터 |
to | 특정 수신번호만 조회 |
limit | 1~1000 (기본 100) |
cursor | 이전 응답의 paging.next_cursor |
JSON
{
"request_id": "req_…",
"data": [
{
"recipient_id": 3304887,
"to": "01011112222",
"status": "DELIVERED",
"client_ref": "A-1001",
"result": { "code": "4100", "message": "전달", "carrier": "SKT" },
"send_at": "2026-11-07T09:00:00+09:00",
"sent_at": "2026-11-07T09:00:02+09:00",
"delivered_at": "2026-11-07T09:00:05+09:00",
"charged": 10.5
}
],
"paging": { "next_cursor": null }
}result.code는 통신사/카카오 결과 코드,result.message는 그 설명입니다. 코드 목록은 결과 코드표 를 참고하세요.REJECTED는 코드가REJECTED로 고정됩니다.charged는 이 수신자에게 청구된 금액입니다. 실패·취소 건은 환불되므로 정산은status와 함께 해석하세요.- 페이지가 더 있으면
paging.next_cursor값이 채워집니다. 그 값을cursor로 넘겨 다음 페이지를 읽고,null이면 끝입니다.
Python — 전체 수신자 결과 수집
def fetch_all_recipients(session, message_id):
cursor, out = None, []
while True:
params = {"limit": 1000}
if cursor:
params["cursor"] = cursor
r = session.get(f"https://api-01.redbean8.io.kr/v1/messages/{message_id}/recipients", params=params, timeout=30)
r.raise_for_status()
body = r.json()
out += body["data"]
cursor = body["paging"]["next_cursor"]
if not cursor:
return out발송 목록
GET /v1/messages
| 쿼리 | 설명 |
|---|---|
from, to | 접수 시각 범위 (ISO 8601). 기본: 최근 7일. 한 번에 최대 31일 |
status | ACCEPTED · SCHEDULED · PROCESSING · PARTIAL · COMPLETED · CANCELLED |
channel | sms · lms · mms · alimtalk · brand |
client_ref | 요청 참조키로 검색 (지정 시 기간 조건은 무시) |
limit | 1~100 (기본 20) |
cursor | 다음 페이지 커서 |
응답은 data 에 발송 건 상세와 같은 항목의 배열, paging.next_cursor 로 구성됩니다. 최신순으로 정렬됩니다.
예약 취소
POST /v1/messages/{message_id}/cancel
예약 시각이 아직 지나지 않았고 발송 대기 중인 수신자를 모두 취소하고 요금을 환불합니다.
JSON — 200 OK
{
"request_id": "req_a2b3c74d17246af7aa5f",
"data": {
"message_id": 1374,
"cancelled": 1,
"refunded": 10.5,
"not_cancellable": 0
}
}cancelled: 취소된 수신자 수,refunded: 환불 금액,not_cancellable: 이미 발송 처리가 시작되어 취소할 수 없는 수신자 수- 취소할 수 있는 건이 하나도 없으면
409 CANCEL_NOT_ALLOWED입니다. (이미 발송 시작 / 이미 취소) - 브랜드메시지는 취소할 수 없습니다 (
422 CANCEL_NOT_ALLOWED). - 취소된 수신자는 Webhook 으로
message.cancelled이벤트가 전송됩니다. - 발송 시각 직전에는 이미 큐에 적재되었을 수 있으므로, 최소 1~2분 전에 취소하세요.
폴링 권장 방식
- 접수 직후부터 최대 수 분간 5~10초 간격으로
GET /v1/messages/{id}를 호출하여counts.pending == 0을 확인합니다. - 대량 발송 결과는
/recipients?status=FAILED처럼 상태로 좁혀 가져오면 응답이 작아집니다. - 조회도 API 키 단위 요청 제한(분당 300회)에 포함됩니다. 가능하면 Webhook 을 사용하세요.