시작하기
알림 게이트웨이 API 개요
알림 게이트웨이 API는 고객사 시스템이 알림톡·문자·이메일을 보내고 발송 결과를 조회하는 RESTful API입니다. 채널마다 발송 경로와 요청 필드만 다르고, 인증·요청 규칙·호출 한도는 모든 채널이 같습니다.
채널
| 채널 | 상태 | 발송 경로 | 문서 |
| 카카오 알림톡 | 제공 중 | /api/v1/alimtalk | 알림톡 |
| 문자(SMS·LMS) | 제공 중 | /api/v1/sms | 문자 |
| 이메일 | 준비 중 | /api/v1/email | 이메일 |
이메일은 준비 중이라 호출하면 501 SEND-5011(해당 채널 발송 기능은 준비 중입니다)로 응답합니다. 그 채널을 쓰도록 허용되지 않은 키는 그보다 먼저 403 RATE-4031을 받습니다.
API 엔드포인트
| 환경 | 엔드포인트 |
| 연동 시험(개발) | https://notify.reinno.com |
| 운영 | 개통할 때 담당자가 안내합니다. |
이 문서의 예시는 주소를 https://{게이트웨이 주소}로 적었습니다. 위 엔드포인트로 바꿔 쓰십시오.
연동 시험 주소에서도 실제로 발송되는지는 주소가 아니라 키가 정합니다(키 환경).
인증
API Key와 Secret Key로 접근 토큰을 받아, API를 호출할 때 Authorization 헤더에 싣습니다. 토큰 하나로 쓰기로 계약한 모든 채널을 호출할 수 있습니다.
- 키 발급 — API Key와 Secret Key는 담당자가 발급합니다. Secret Key는 발급할 때 한 번만 보여 주므로 서버의 환경 변수나 비밀 저장소에 보관하고, 소스 코드·저장소·로그에 남기지 마십시오. 유출이 의심되면 바로 담당자에게 폐기·재발급을 요청하십시오.
- 토큰 발급 — 토큰 발급을 호출해 접근 토큰(30분 유효)과 갱신 토큰(14일 유효)을 받습니다.
- API 호출 —
Authorization: Bearer {accessToken} 헤더를 붙여 호출합니다.
- 토큰 갱신 — 접근 토큰이 만료되기 전에 토큰 갱신이나 토큰 발급을 다시 호출합니다.
키 환경
API Key는 발급할 때 환경이 정해지고, 환경에 따라 실제 발송 여부가 다릅니다. 환경은 바꿀 수 없으며 바꾸려면 새로 발급받습니다.
| 키 환경 | 실제 발송 | 발송 쿼터 | 발송 응답 |
| 운영 | 나갑니다 | 차감합니다 | SEND-0000 |
| 검증 | 나갑니다 | 차감합니다 | SEND-0000 |
| 개발 | 나가지 않습니다(모의 접수) | 차감하지 않습니다 | SEND-0003 |
- 개발 키는 인증·권한·호출 한도와 필수값·치환 변수 검사까지 운영과 같이 거친 뒤, 발송만 하지 않고
SEND-0003(모의 발송으로 접수했습니다)으로 답합니다. 번호 형식·본문 길이처럼 발송할 때 하는 검사는 거치지 않으니, 최종 확인은 검증 키로 합니다.
- 개발 키로 받은 메시지 키와 배치 키는
mock-으로 시작합니다. 발송되지 않았으므로 결과 조회에서는 찾을 수 없습니다(404).
- 실제로 받아 보는 시험은 검증 키로 합니다. 그 건은 쿼터가 차감됩니다.
문서 구성
| 묶음 | 내용 |
| 공통 | 모든 채널에 똑같이 적용됩니다. 토큰 발급·갱신, 요청·응답 규칙, 호출 한도, 멱등 키·다시 보내기, 공통 오류 코드, 운영자 재발송·예약 취소 |
| 채널별 | 채널마다 다른 것입니다. 발송, 결과 조회, 데이터 타입, 상태·결과 코드, 채널 오류 코드, 주의 사항 |
오퍼레이션
| 분류 | 오퍼레이션 | 메서드·경로 |
| 공통 | 토큰 발급 | POST /oauth/token |
| 토큰 갱신 | POST /oauth/token |
| 알림톡 | 단건 발송 | POST /api/v1/alimtalk |
| 대량 발송 | POST /api/v1/alimtalk/batches |
| 결과 단건 조회 | GET /api/v1/alimtalk/messages/{messageKey} |
| 결과 목록 조회 | GET /api/v1/alimtalk/messages |
| 예약 취소 | DELETE /api/v1/alimtalk/messages/{messageKey} |
| 대량 예약 취소 | DELETE /api/v1/alimtalk/messages/batches/{batchKey} |
| 문자 | 단건 발송 | POST /api/v1/sms |
| 대량 발송 | POST /api/v1/sms/batches |
| 결과 단건 조회 | GET /api/v1/sms/messages/{messageKey} |
| 결과 목록 조회 | GET /api/v1/sms/messages |
| 예약 취소 | DELETE /api/v1/sms/messages/{messageKey} |
| 대량 예약 취소 | DELETE /api/v1/sms/messages/batches/{batchKey} |
| 이메일 | 준비 중 |
발송 결과는 호출하는 쪽이 조회해서 가져갑니다(폴링). 게이트웨이가 결과를 먼저 보내 주는 방식(웹훅)은 없습니다.
이 문서는 2026년 10월 26일 기준입니다.
시작하기
연동 순서
처음 연동할 때 따라 하는 순서입니다. 토큰을 받고, 메시지를 보내고, 결과를 가져옵니다. 예시는 알림톡이며 다른 채널도 순서는 같습니다.
1. 준비물 받기
담당자에게 다음을 받습니다.
- API Key와 Secret Key — 실제로 받아 보는 시험에는 검증 키를 받습니다(개발 키는 발송하지 않습니다, 키 환경)
- (알림톡) 카카오 비즈니스 채널의 발신 프로필 키(40자)
- (알림톡) 카카오가 승인한 템플릿의 코드와 본문
- (문자) 받은 API Key로 쓸 수 있게 등록된 발신번호 — 발신번호는 API Key가 속한 프로젝트마다 등록됩니다
- 연동 시험 주소와 하루·한 달 발송 쿼터
2. 토큰 발급
curl -X POST 'https://{게이트웨이 주소}/oauth/token' -H 'Content-Type: application/json' -d '{"grantType":"client_credentials","apiKey":"{API Key}","secretKey":"{Secret Key}"}'
응답의 data.accessToken을 30분 동안 재사용합니다(토큰 발급).
3. 알림톡 보내기
대량 발송 API로 한 명에게 보내는 예입니다. Idempotency-Key는 요청마다, itemIdempotencyKey는 수신자마다 새로 만들고 저장해 둡니다.
curl -X POST 'https://{게이트웨이 주소}/api/v1/alimtalk/batches' -H 'Authorization: Bearer {accessToken}' -H 'Content-Type: application/json' -H 'Idempotency-Key: 0c9e7d1a-5b2f-4a38-8e61-3f2d9c4b7a10' -d '{
"template": {
"senderKey": "{발신 프로필 키 40자}",
"callbackNo": "0212345678",
"templateCode": "SURVEY_START_01",
"body": "#{name}님, 『#{surveyName}』 설문이 열렸습니다."
},
"items": [
{ "recvNo": "01012345678", "itemIdempotencyKey": "talk7788-25780", "clientRef": "25780",
"variables": { "name": "홍길동", "surveyName": "도민 만족도 조사" } }
]
}'
응답의 data.batchKey와 항목별 messageId를 저장합니다. messageId가 없는 항목은 접수되지 않았습니다(대량 발송).
4. 결과 가져오기
curl -G 'https://{게이트웨이 주소}/api/v1/alimtalk/messages' -H 'Authorization: Bearer {accessToken}' --data-urlencode 'batchKey={batchKey}' --data-urlencode 'size=100'
1분쯤 간격으로, 모든 건이 SUCCESS·FAIL·CANCELLED 중 하나가 될 때까지 조회합니다. 결과는 clientRef로 내 쪽 기록과 맞춥니다(목록 조회).
5. 토큰 관리
- 접근 토큰이 만료되기 전에 토큰 갱신하거나 토큰 발급을 다시 호출합니다.
- 호출 중에
401 AUTH-4013(만료)을 받으면 토큰을 새로 받은 뒤 다시 호출합니다.
Java 예제
Java 8과 Jackson으로 위 순서를 그대로 옮긴 예입니다. 키는 환경 변수에서 읽습니다.
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.UUID;
/** 알림 게이트웨이 연동 예제(Java 8, Jackson). 토큰 발급 → 대량 발송 → 결과 조회 */
public class NotifyGatewayExample {
private static final String BASE_URL = "https://notify.reinno.com";
private static final ObjectMapper JSON = new ObjectMapper();
public static void main(String[] args) throws IOException {
// 1) 토큰 발급. 받은 토큰은 만료(30분) 전까지 재사용한다.
ObjectNode tokenReq = JSON.createObjectNode();
tokenReq.put("grantType", "client_credentials");
tokenReq.put("apiKey", System.getenv("NOTIFY_API_KEY"));
tokenReq.put("secretKey", System.getenv("NOTIFY_SECRET_KEY"));
JsonNode token = call("POST", "/oauth/token", null, null, tokenReq);
String accessToken = token.path("data").path("accessToken").asText();
// 2) 대량 발송(템플릿 방식, 1건)
ObjectNode body = JSON.createObjectNode();
ObjectNode template = body.putObject("template");
template.put("senderKey", System.getenv("NOTIFY_SENDER_KEY"));
template.put("callbackNo", "0212345678");
template.put("templateCode", "SURVEY_START_01");
template.put("body", "#{name}님, 『#{surveyName}』 설문이 열렸습니다.");
ObjectNode item = body.putArray("items").addObject();
item.put("recvNo", "01012345678");
item.put("itemIdempotencyKey", UUID.randomUUID().toString()); // 다시 보낼 때 같은 값을 쓰도록 저장해 둔다
item.put("clientRef", "25780");
ObjectNode vars = item.putObject("variables");
vars.put("name", "홍길동");
vars.put("surveyName", "도민 만족도 조사");
JsonNode sent = call("POST", "/api/v1/alimtalk/batches", accessToken,
UUID.randomUUID().toString(), body);
String batchKey = sent.path("data").path("batchKey").asText();
for (JsonNode r : sent.path("data").path("results")) {
if (!r.hasNonNull("messageId")) { // messageId 가 없으면 접수되지 않은 항목
System.out.println("거절 index=" + r.path("index").asInt()
+ " " + r.path("errorCode").asText() + " " + r.path("errorMessage").asText());
}
}
// 3) 결과 조회. 실제로는 1분쯤 간격으로, 모든 건이 확정될 때까지 반복한다.
JsonNode list = call("GET", "/api/v1/alimtalk/messages?size=100&batchKey="
+ URLEncoder.encode(batchKey, "UTF-8"), accessToken, null, null);
for (JsonNode m : list.path("data").path("content")) {
System.out.println(m.path("clientRef").asText() + " " + m.path("status").asText()
+ " " + m.path("resultCode").asText("-"));
}
}
private static JsonNode call(String method, String path, String accessToken,
String idempotencyKey, JsonNode body) throws IOException {
HttpURLConnection conn = (HttpURLConnection) new URL(BASE_URL + path).openConnection();
conn.setRequestMethod(method);
conn.setConnectTimeout(5000);
conn.setReadTimeout(40000); // 대량 발송은 응답까지 최대 35초가 걸릴 수 있다
if (accessToken != null) {
conn.setRequestProperty("Authorization", "Bearer " + accessToken);
}
if (idempotencyKey != null) {
conn.setRequestProperty("Idempotency-Key", idempotencyKey);
}
if (body != null) {
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
try (OutputStream out = conn.getOutputStream()) {
out.write(JSON.writeValueAsBytes(body));
}
}
int status = conn.getResponseCode();
InputStream in = status < 400 ? conn.getInputStream() : conn.getErrorStream();
JsonNode res = in == null ? JSON.createObjectNode() : JSON.readTree(in);
if (status >= 400) {
// 형식 A 는 code, 형식 B 는 error.code 에 오류 코드가 있다.
String code = res.has("code") ? res.path("code").asText() : res.path("error").path("code").asText();
throw new IOException("HTTP " + status + " " + code + " traceId=" + res.path("traceId").asText());
}
return res;
}
}
연동 점검표
- Secret Key를 소스 코드에 넣지 않았다.
- 토큰을 재사용하고, 만료 전에 갱신한다.
- 발송 요청마다 새
Idempotency-Key를 만든다(1~64자).
- 대량 발송 항목마다
itemIdempotencyKey를 만들어 저장하고, 다시 보낼 때 같은 값을 쓴다.
batchKey·messageId·clientRef를 저장한다.
- 응답의
messageId가 없는 항목을 실패로 처리한다.
- 결과는 1분쯤 간격으로 조회하고,
SUCCESS·FAIL·CANCELLED만 최종으로 본다.
- 오류별로 다시 보낼지를 다시 보내기 표대로 판단한다.
공통
토큰 발급
API Key와 Secret Key로 접근 토큰(access token)을 발급받습니다. 발송·조회 API는 이 토큰을 Authorization 헤더에 실어 호출합니다.
참고
- 접근 토큰은 30분(
expiresIn 1800초) 동안 유효합니다. 요청마다 새로 받지 말고 만료 전까지 재사용하십시오.
- 토큰 발급은 호출 IP당 분당 10회로 제한됩니다. 같은 IP를 쓰는 서버가 여러 대면 이 한도를 함께 씁니다.
- 요청 본문은 JSON만 받습니다. 필드 이름은
grantType처럼 낙타 표기입니다(grant_type 아님).
요청
POST/oauth/token
| 헤더 | 필수 여부 | 설명 |
Content-Type | 필수 | application/json |
이 API는 Authorization과 Idempotency-Key가 필요 없습니다.
요청 바디
| 필드 | 타입 | 필수 여부 | 설명 |
grantType | String | 필수 | client_credentials |
apiKey | String | 필수 | 발급받은 API Key. 최대 64자 |
secretKey | String | 필수 | API Key와 함께 발급받은 Secret Key. 최대 128자 |
요청 예시
curl -X POST 'https://{게이트웨이 주소}/oauth/token' \
-H 'Content-Type: application/json' \
-d '{"grantType":"client_credentials","apiKey":"{API Key}","secretKey":"{Secret Key}"}'
응답
응답 바디
| 필드 | 타입 | 설명 |
success | Boolean | 성공하면 true |
code | String | 결과 코드. 성공은 AUTH-0000 |
message | String | 결과 메시지 |
data.accessToken | String | 접근 토큰(JWT). 발송·조회 API의 Authorization: Bearer {accessToken}에 씁니다. |
data.tokenType | String | 항상 Bearer |
data.expiresIn | Integer | 접근 토큰 유효 시간(초). 1800 |
data.refreshToken | String | 갱신 토큰(rt_로 시작). 14일 동안 토큰 갱신에 쓸 수 있습니다. |
data.scope | String | 참고용 값입니다. 실제로 쓸 수 있는 채널은 계약에 따라 정해집니다. |
traceId | String | 요청 추적 ID |
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | AUTH-0000 | 토큰이 발급되었습니다. |
| 400 | CMN-4001 | 필수 필드가 없거나, grantType 값이 틀리거나, JSON 형식이 깨졌습니다. 필드 오류는 data.fieldErrors[](field, message)에 담깁니다. |
| 401 | AUTH-4012 | API 자격증명이 올바르지 않습니다(없는 키, 틀린 Secret Key, 폐기된 키). |
| 403 | CMN-4031 | 접근 권한이 없습니다(고객사 이용 정지·해지). |
| 429 | RATE-4291 | 토큰 발급 한도(IP당 분당 10회) 초과. Retry-After: 60 |
| 500 | CMN-5001 | 일시적인 오류. 잠시 뒤 다시 호출합니다. |
응답 예시
{
"success": true,
"code": "AUTH-0000",
"message": "토큰이 발급되었습니다.",
"data": {
"accessToken": "eyJraWQiOiJub3RpZnktYXV0aC0uLi4iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOi...",
"tokenType": "Bearer",
"expiresIn": 1800,
"refreshToken": "rt_3q2-7wKx0bVd9mZ4u1Yk8fJcLhTnRpAsQeGiOyWvBxE",
"scope": "send:sms send:email send:fax send:alimtalk"
},
"traceId": "4c1f0a9d2b7e3c55"
}
공통
토큰 갱신
갱신 토큰으로 새 접근 토큰과 새 갱신 토큰을 받습니다. Secret Key를 다시 보내지 않아도 됩니다.
참고
- 갱신할 때마다 새 갱신 토큰이 나오고, 방금 쓴 갱신 토큰은 더 이상 쓸 수 없습니다. 응답으로 받은 새 갱신 토큰으로 바꿔 저장하십시오.
- 이미 쓴 갱신 토큰을 다시 보내면 그 토큰에서 이어진 갱신 토큰이 모두 폐기되고
AUTH-4016이 옵니다. 이때는 토큰 발급부터 다시 합니다.
- 갱신 토큰은 14일 동안 유효하며, 갱신할 때마다 새로 14일이 주어집니다.
- 갱신 대신 접근 토큰이 만료되기 전에 토큰 발급을 다시 호출해도 됩니다. 서버가 여러 대라 갱신 토큰을 함께 관리하기 어렵다면 이쪽이 단순합니다.
요청
POST/oauth/token
| 헤더 | 필수 여부 | 설명 |
Content-Type | 필수 | application/json |
요청 바디
| 필드 | 타입 | 필수 여부 | 설명 |
grantType | String | 필수 | refresh_token |
apiKey | String | 필수 | 갱신 토큰을 발급받은 API Key. 최대 64자 |
refreshToken | String | 필수 | 마지막으로 받은 갱신 토큰. 최대 512자 |
요청 예시
curl -X POST 'https://{게이트웨이 주소}/oauth/token' \
-H 'Content-Type: application/json' \
-d '{"grantType":"refresh_token","apiKey":"{API Key}","refreshToken":"{refreshToken}"}'
응답
응답 바디는 토큰 발급과 같습니다.
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | AUTH-0000 | 토큰이 발급되었습니다. |
| 400 | CMN-4001 | 필수 필드가 없거나 형식이 틀렸습니다. |
| 401 | AUTH-4014 | 갱신 토큰이 없거나 올바르지 않습니다. |
| 401 | AUTH-4013 | 갱신 토큰이 만료되었습니다. 토큰 발급부터 다시 합니다. |
| 401 | AUTH-4016 | 이미 쓴 갱신 토큰이거나 폐기되었습니다(키 폐기, 이용 정지 포함). 토큰 발급부터 다시 합니다. |
| 401 | AUTH-4012 | API 키가 폐기되었습니다. |
| 403 | CMN-4031 | 갱신 토큰과 apiKey가 맞지 않거나 고객사 이용이 정지되었습니다. |
| 429 | RATE-4291 | 호출 한도(IP당 분당 60회) 초과. Retry-After: 60 |
공통
요청·응답 규칙
모든 채널의 API에 똑같이 적용되는 요청 헤더와 응답 형식입니다.
| 헤더 | 필수 여부 | 설명 |
Authorization | 필수 (토큰 발급 제외) | Bearer {accessToken}
Bearer는 대문자 B로 시작하고 뒤에 공백 한 칸을 둡니다.
|
Content-Type | 조건부 | POST 요청은 application/json |
Idempotency-Key | 조건부 | 발송 API(POST)에 필수. 요청을 구분하는 고유한 값입니다.
- 영문 대소문자·숫자·하이픈(
-)만, 1~64자. UUID를 권장합니다.
- 같은 키로 24시간 안에 다시 보내면 새로 발송하지 않고 첫 응답을 그대로 돌려줍니다(멱등 키 참조).
|
X-Trace-Id | 선택 | 요청 추적 ID. 영문·숫자·하이픈 1~64자. 이 모양으로 보내면 그 값을 그대로 써서, 응답 헤더와 응답 본문의 traceId에 같은 값이 돌아옵니다. 보내지 않거나 모양이 다르면 게이트웨이가 새로 만듭니다. |
응답
API에 따라 응답 형식이 두 가지입니다.
| 필드 | 타입 | 설명 |
success | Boolean | HTTP 상태가 400 미만이면 true |
code | String | 결과 코드(오류 코드) |
message | String | 결과 메시지 |
data | Object | 결과 데이터. 오류일 때는 대개 null |
traceId | String | 요청 추적 ID |
{
"success": false,
"code": "AUTH-4013",
"message": "인증 토큰이 만료되었습니다. 다시 로그인하세요.",
"data": null,
"traceId": "4c1f0a9d2b7e3c55"
}
지금은 결과 조회 API와 예약 취소 API(둘 다 알림톡·문자)가 이 형식을 씁니다. 다른 채널의 조회 형식은 각 채널 문서에 적습니다.
| 필드 | 타입 | 설명 |
ok | Boolean | 성공하면 true |
data | Object | 결과 데이터. 성공일 때만 있습니다. |
error.code | String | 오류 코드. 실패일 때만 있습니다. |
error.message | String | 오류 메시지 |
traceId | String | 요청 추적 ID |
주의
- 형식 B는 값이 없는 필드를
null로 보내지 않고 키 자체를 뺍니다.
- 조회·취소 API라도 토큰 오류(401)나 일시 장애(503)처럼 게이트웨이에서 막힌 응답은 형식 A로 옵니다. 응답에
success가 있는지 ok가 있는지로 구분하십시오.
| 헤더 | 언제 | 설명 |
X-Trace-Id | 모든 응답 | 요청 추적 ID. 문의할 때 알려 주십시오. |
X-Result-Code | 오류 응답, 일부 거절(SEND-0002), 모의 접수(SEND-0003) | 본문의 code와 같은 값 |
X-RateLimit-Limit
X-RateLimit-Remaining | 발송 API | 호출 한도와 남은 횟수 |
Retry-After | 409, 429 | 다시 호출하기까지 기다릴 초 |
X-Quota-Limit
X-Quota-Remaining
X-Quota-Period | 쿼터 초과(429 RATE-4292) | 넘어선 한도, 남은 건수(0), 기간(daily | monthly) |
X-Message-Key | 단건 발송 | 본문의 messageId와 같은 값 |
X-Batch-Key | 대량 발송 | 본문의 batchKey와 같은 값 |
Idempotent-Replayed | 멱등 재반환 | true면 같은 Idempotency-Key의 첫 응답을 다시 돌려준 것입니다. |
응답 상태 코드
| HTTP | 설명 |
| 200 | 요청 성공(발송은 "접수" 성공 — 전달 결과는 각 채널의 결과 조회로 확인) |
| 400 | 요청 형식·값 오류 |
| 401 | 인증 실패(토큰 없음·만료·위조, 키 오류) |
| 403 | 권한 없음(채널 미허용, 고객사·프로젝트 이용 정지) |
| 404 | 없는 경로 또는 없는 대상 |
| 409 | 같은 Idempotency-Key 요청이 아직 처리 중 |
| 429 | 호출 한도 또는 발송 쿼터 초과 |
| 500 | 서버 오류 |
| 501 | 준비 중인 채널 |
| 502 / 504 | 발송 채널 처리 오류 / 응답 지연 |
| 503 | 일시 장애 |
코드별 의미와 대처는 오류 코드를 참조해 주십시오.
시간대
- 응답의 시각은 모두 한국 시간(
+09:00)이 붙은 ISO 8601입니다.
- 예약 시각처럼 요청에 넣는 일시에도 시간대를 반드시 붙입니다(예:
2026-10-10T09:00:00+09:00).
- 발송 쿼터의 하루·한 달 경계도 한국 시간 기준입니다.
응답 본문의 traceId와 응답 헤더 X-Trace-Id, 그리고 messageId(또는 batchKey)와 받은 code를 함께 알려 주시면 원인을 빨리 찾을 수 있습니다.
공통
호출 한도
API Key와 프로젝트별로 호출 횟수와 발송 건수에 한도가 있습니다. 하루·한 달 발송 쿼터와 계약 초당 한도는 담당자가 안내합니다.
한도
| 대상 | 한도 | 초과하면 |
| 발송 API | API Key별 초당 20회, 분당 600회. 대량 발송 한 번도 1회로 셉니다. | 429 RATE-4291 |
| 계약 초당 한도 | 프로젝트·채널별로 계약한 초당 요청 수(있는 경우) | 429 RATE-4293 |
| 발송 쿼터 | 프로젝트·채널별 하루·한 달 발송 건수. 대량 발송은 항목 수만큼 셉니다. 날짜는 한국 시간 기준입니다. | 429 RATE-4292 |
| 대량 발송 크기 | 요청당 최대 500건 | 400 CMN-4001 |
| 알림톡 결과 조회 API | API Key별 초당 60회(단건·목록 합산) | 429 RATE-4290 |
| 문자 결과 조회 API | API Key별 초당 60회(단건·목록 합산, 알림톡 조회와 따로 셉니다) | 429 RATE-4290 |
| 알림톡 예약 취소 API | API Key별 초당 60회(단건·대량 합산) | 429 RATE-4290 |
| 토큰 발급 | 호출 IP당 분당 10회 | 429 RATE-4291 |
주의
발송 쿼터는 요청을 받는 순간 먼저 차감되고, 성공한 건만 남습니다. 발송이 실패하거나 확인되지 않은 건, 취소한 예약, 대량 발송에서 거절된 항목의 쿼터는 결과가 나온 뒤 돌아옵니다(보통 몇 분 안). 결과를 기다리는 동안(예약은 발송할 때까지)은 차감된 채로 셉니다. 같은 Idempotency-Key로 다시 보낸 요청은 다시 차감하지 않고, 개발 키로 보낸 요청은 차감하지 않습니다. 운영자가 콘솔에서 다시 보낸 건도 보통 발송과 같이 차감합니다. 하루·한 달 한도와 계약 초당 한도는 담당자가 안내합니다.
한도를 넘었을 때
Retry-After 초만큼 기다린 뒤 새 Idempotency-Key로 다시 보냅니다. 같은 키로 보내면 같은 429가 24시간 동안 돌아옵니다.
RATE-4292(쿼터 초과)는 응답 헤더 X-Quota-Period(daily | monthly)로 어느 한도인지 알 수 있습니다. 하루 쿼터는 한국 시간 0시, 한 달 쿼터는 매월 1일에 초기화됩니다.
- 발송 API 응답의
X-RateLimit-Remaining으로 남은 호출 횟수를 볼 수 있습니다.
공통
멱등 키와 다시 보내기
같은 발송 요청이 두 번 처리되지 않게 하는 규칙과, 오류를 받았을 때 다시 보내는 방법입니다.
Idempotency-Key
- 발송 요청마다 새로 만듭니다. 영문·숫자·하이픈 1~64자이고 UUID를 권장합니다.
- 같은 키로 24시간 안에 다시 보내면 새로 처리하지 않고 첫 응답을 그대로 돌려줍니다(응답 헤더
Idempotent-Replayed: true). 본문이 달라도 마찬가지이므로 다른 요청에 같은 키를 쓰지 마십시오.
- 오류 응답도 24시간 동안 그대로 돌려줍니다. 오류를 받은 뒤 고쳐서 다시 보낼 때는 새 키를 씁니다. 단
409와 5xx는 저장하지 않습니다 — 둘 다 결과가 아직 나오지 않았다는 뜻이라, 같은 키로 다시 보내면 결과를 확인할 수 있습니다.
- 첫 요청이 아직 처리 중일 때 같은 키로 보내면
409(RATE-4091 또는 SEND-4007)가 옵니다. Retry-After 뒤 같은 키로 다시 보내면 첫 응답을 받습니다.
- 같은 키로 다른 내용을 보내면 첫 응답을 돌려주지 않고
400 RATE-4002(또는 SEND-4002)로 거절합니다. 새 요청에는 새 키를 씁니다. 같은 요청을 다시 직렬화한 것(필드 순서·공백만 다름)은 같은 요청으로 봅니다.
다시 보내기
| 받은 응답 | 접수 여부 | 다시 보낼 때 |
200 SEND-0002 | 일부 접수 | 거절된 항목만 고쳐서 새 Idempotency-Key로 |
400 (RATE-4001 제외), 401, 403 | 접수 안 됨 | 원인을 고친 뒤 새 키로 |
400 RATE-4001 | 접수 안 됨 | Idempotency-Key를 붙여서 |
400 RATE-4002 | 이번 요청은 접수 안 됨(그 키의 첫 요청은 그대로) | 새 키로 |
409 RATE-4091, SEND-4007 | 처리 중 | Retry-After 뒤 같은 키로 |
429 RATE-4291, RATE-4293 | 접수 안 됨 | Retry-After 뒤 새 키로 |
429 RATE-4292 | 접수 안 됨 | 쿼터가 초기화된 뒤(한국 시간 0시, 매월 1일) 새 키로 |
501 SEND-5011 | 접수 안 됨 | 다시 보내지 않습니다. |
503 SEND-5031 | 접수 안 됨(발송 채널을 잠시 쓸 수 없어 보내지 않았음) | Retry-After 뒤 같은 키로 |
| 500, 502, 503, 504, 또는 응답 없음 | 알 수 없음 | 단건 발송은 같은 Idempotency-Key로 다시 보냅니다. 대량 발송은 채널 문서를 따릅니다(알림톡). |
대량 발송의 항목 멱등 키처럼 채널마다 다른 규칙은 각 채널의 주의 사항에 있습니다(알림톡, 문자).
공통
공통 오류 코드
채널과 상관없이 나오는 코드입니다. 채널별 발송·조회 오류는 각 채널의 오류 코드에 있습니다(알림톡, 문자). 응답 형식은 요청·응답 규칙을 참조해 주십시오.
게이트웨이·인증 (형식 A)
| 코드 | HTTP | 메시지 | 대처 |
CMN-4011 | 401 | 인증이 필요합니다. | Authorization: Bearer {accessToken}를 붙입니다. |
AUTH-4012 | 401 | API 자격증명이 올바르지 않습니다. | API Key·Secret Key를 확인합니다. 키가 폐기됐으면 담당자에게 재발급을 요청합니다. |
AUTH-4013 | 401 | 인증 토큰이 만료되었습니다. 다시 로그인하세요. | 토큰을 갱신하거나 다시 발급받습니다. |
AUTH-4014 | 401 | 인증 토큰이 유효하지 않습니다. | 토큰을 다시 발급받습니다. |
AUTH-4015 | 401 | 인증 키 구성이 일치하지 않습니다. | 토큰이 깨졌거나 다른 환경의 토큰입니다. 다시 발급받습니다. |
AUTH-4016 | 401 | 세션이 만료되어 재인증이 필요합니다. | 갱신 토큰을 쓸 수 없습니다. 토큰 발급부터 다시 합니다. |
CMN-4031 | 403 | 접근 권한이 없습니다. | 고객사 이용이 정지·해지됐거나 갱신 토큰의 키가 다릅니다. |
RATE-4001 | 400 | 멱등성 키가 필요합니다. | 발송 요청에 Idempotency-Key를 붙입니다(영문·숫자·하이픈 1~64자). |
RATE-4002 | 400 | 같은 Idempotency-Key 로 다른 요청을 보냈습니다. 새 요청에는 새 키를 쓰세요. | 이 키로 먼저 보낸 요청과 내용이 다릅니다. 새 요청에는 새 Idempotency-Key를 씁니다. 먼저 보낸 요청은 그대로입니다. |
RATE-4091 | 409 | 같은 요청이 처리 중입니다. 잠시 후 같은 키로 다시 확인하세요. | 같은 키 요청이 아직 처리 중입니다. Retry-After 뒤 같은 키로 다시 보내면 첫 응답을 받습니다. |
RATE-4291 | 429 | 요청 한도를 초과했습니다. 잠시 후 다시 시도하세요. | Retry-After 초 뒤 새 Idempotency-Key로 다시 보냅니다. |
RATE-4292 | 429 | 발송 쿼터를 모두 사용했습니다. 한도 초기화 후 다시 시도하세요. | 하루·한 달 쿼터가 찼습니다(X-Quota-Period). 초기화 뒤 보내거나 담당자에게 문의합니다. |
RATE-4293 | 429 | 계약된 초당 요청 한도를 초과했습니다. 잠시 후 다시 시도하거나 요금제 상향을 문의하세요. | 호출 속도를 낮춥니다. |
RATE-4031 | 403 | 해당 채널 발송 권한이 없습니다. | 이 채널을 쓰도록 허용되지 않았습니다. 담당자에게 문의합니다. |
RATE-4032 | 403 | 고객사 이용이 정지되어 발송할 수 없습니다. 담당자에게 문의하세요. | 담당자에게 문의합니다. |
RATE-4033 | 403 | 해지된 고객사입니다. 발송할 수 없습니다. | - |
RATE-4034 | 403 | 프로젝트가 정지되어 발송할 수 없습니다. 담당자에게 문의하세요. | 담당자에게 문의합니다. |
RATE-4035 | 403 | 폐기된 프로젝트입니다. 발송할 수 없습니다. | - |
CMN-4001 | 400 | 배치 항목 수가 허용 범위를 초과했습니다. | 대량 발송을 500건 이하로 나눕니다. |
CMN-4041 | 404 | 요청한 리소스를 찾을 수 없습니다. | 경로와 메서드를 확인합니다. |
CMN-5031 | 503 | 일시적인 오류가 발생했습니다. 잠시 후 다시 시도하세요. | 발송이었다면 접수됐을 수 있습니다(다시 보내기). |
CMN-5001 | 500 | 일시적인 오류가 발생했습니다. 잠시 후 다시 시도하세요. | 잠시 뒤 다시 시도합니다. |
발송 공통 (형식 A)
채널과 상관없이 발송 API에서 나오는 코드입니다.
| 코드 | HTTP | 메시지 | 대처 |
SEND-0000 | 200 | 요청이 접수되었습니다. / 배치 요청이 접수되었습니다. | - |
SEND-0003 | 200 | 모의 발송으로 접수했습니다. 개발 키로 보낸 요청은 실제로 발송되지 않습니다. | 실제로 보내려면 검증·운영 키를 씁니다(키 환경). |
SEND-4007 | 409 | 같은 요청이 처리 중입니다. 잠시 후 다시 확인하세요. | Retry-After 뒤 같은 키로 다시 보내면 처음 접수 결과를 받습니다. |
SEND-0002 | 200 | {n}건 접수, {m}건 거절되었습니다. | 항목별 결과에서 거절된 항목을 확인합니다. |
CMN-4001 | 400 | 입력값이 올바르지 않습니다. (또는 구체적인 이유) | data.fieldErrors[]나 message를 보고 고친 뒤 새 키로 보냅니다. |
SEND-4002 | 400 | 필드 형식 오류(수신번호·발신번호 등, 구체적인 이유) | 채널이 필드 형식을 거절했습니다. message를 보고 고친 뒤 새 키로 보냅니다. |
SEND-4003 | 400 | 본문 길이·형식 오류(구체적인 이유) | 채널이 본문을 거절했습니다. message를 보고 고친 뒤 새 키로 보냅니다. |
SEND-4006 | 400 | 치환 변수 오류(치환 변수) | variables를 고칩니다. |
SEND-4008 | 400 | 등록되지 않은 발신번호입니다. 등록된 발신번호로 보내거나 담당자에게 등록을 요청하세요. | 문자에만 해당합니다(알림톡은 발신번호 등록 여부를 보지 않습니다). 등록한 발신번호로 보냅니다. 발신번호는 API Key가 속한 프로젝트마다 등록되므로, 다른 API Key로 보낼 수 있던 번호가 이 API Key로는 막힐 수 있습니다 — 그때는 담당자에게 이 API Key에서도 쓸 수 있게 요청합니다. 새로 등록했으면 새 Idempotency-Key로 보냅니다 — 같은 키는 이 응답을 그대로 돌려줍니다. |
SEND-5011 | 501 | 해당 채널 발송 기능은 준비 중입니다. | 이메일·팩스는 아직 쓸 수 없습니다. |
SEND-5021 | 502 | 발송 채널 처리 중 오류가 발생했습니다. | 접수 여부를 알 수 없습니다(다시 보내기). |
SEND-5031 | 503 | 발송 채널을 잠시 쓸 수 없어 보내지 않았습니다. 잠시 후 같은 키로 다시 보내세요. | 보내지 않았습니다. Retry-After 뒤 같은 키로 다시 보냅니다. |
SEND-5041 | 504 | 발송 채널 응답이 지연되었습니다. | 접수 여부를 알 수 없습니다. |
RATE-4291 | 429 | 요청 한도를 초과했습니다. 잠시 후 다시 시도하세요. | 잠시 뒤 새 키로 다시 보냅니다. |
공통
운영자 재발송·예약 취소
운영자는 콘솔에서 고객사의 실패 건을 다시 보내거나, 대기 중인 예약을 취소할 수 있습니다. 둘 다 고객사의 API Key로 처리되어 결과 조회에 그대로 보입니다. 고객사가 따로 호출할 것은 없지만, 결과를 내 쪽 기록과 맞출 때 알아 두어야 합니다.
재발송
- 운영자가 고른 실패 건만 다시 보냅니다. 자동으로 다시 보내지는 않습니다.
- 고객사의 API Key로 새로 발송합니다. 원래 메시지는
FAIL 그대로 남고, 다시 보낸 메시지는 새 메시지 키를 받습니다.
- 원래 보낸 본문·버튼·발신 정보 그대로, 예약 없이 바로 보냅니다.
- 원래 호출이 대량 발송이면 대량 발송으로 보내고
clientRef를 그대로 싣습니다. batchKey는 새로 생깁니다. 원래 호출이 단건이면 단건으로 보냅니다.
- 보통 발송과 똑같이 발송 쿼터를 씁니다.
다시 보내지 않는 건
- 결과가 실패가 아닌 건 — 대기·성공·취소, 결과를 확인하지 못한 건
- 다시 보내도 같은 결과인 건 — 수신번호 오류, 수신 차단
- 실제로는 도착했을 수 있는 건 — 알림톡
FAIL_TIME 중 도착 여부가 불확실한 건(FAIL_TIME 구분)
- 보낸 지 30일이 지난 건(예약 발송은 예약 시각부터 셉니다)
- 마지막 재발송이 이미 접수된 건
결과를 맞출 때
- 알림톡 결과를 기간으로 목록 조회하면 다시 보낸 메시지가 한 건 더 보입니다. 대량 발송이었다면 같은
clientRef로 보입니다. 같은 clientRef의 메시지 중 하나라도 SUCCESS면 전달된 것으로 보십시오.
- 원래
batchKey로만 조회하면 다시 보낸 메시지는 나오지 않습니다.
- 실패 건을 직접 다시 보내기 전에 담당자와 확인하십시오. 운영자 재발송과 겹치면 두 번 나갑니다.
예약 취소
- 운영자는 콘솔에서 대기 중인 예약을 한 건씩, 또는 대량 발송 단위로 모두 취소할 수 있습니다. 취소된 메시지는
CANCELLED로 조회됩니다.
- 고객사 이용이 정지·해지되면 남은 예약은 모두 자동으로 취소됩니다. 이용을 다시 열어도 취소된 예약은 되살아나지 않으니, 필요하면 새로 예약합니다.
- 프로젝트 정지나 키 폐기만으로는 남은 예약이 취소되지 않습니다.
- 어느 경우든 취소된 건의 발송 쿼터는 돌아옵니다(취소 뒤 몇 분 안).
고객사가 직접 취소할 때는 각 채널의 예약 취소 API를 씁니다(알림톡, 문자).
알림톡
알림톡 단건 발송
수신자 한 명에게 알림톡을 보냅니다. 응답은 접수 결과이며, 실제로 전달됐는지는 발송 결과 조회로 확인합니다.
참고
- 카카오 비즈니스 채널의 발신 프로필 키와 카카오가 승인한 알림톡 템플릿이 있어야 합니다. 본문은 승인된 템플릿과 같아야 하고 변수 자리만 달라질 수 있습니다.
- 발송 쿼터를 1건 차감합니다. 성공하지 않으면 결과가 나온 뒤 돌아옵니다.
- 응답을 못 받았을 때는 같은
Idempotency-Key로 다시 보내십시오. 먼저 보낸 요청이 접수됐으면 처음의 messageId를 그대로 돌려받고, 접수되지 않았으면 그때 발송됩니다. 아직 처리 중이면 409(SEND-4007)와 Retry-After가 오니 그 초만큼 기다린 뒤 같은 키로 다시 확인하십시오. 새 키로 바꿔 보내지 마십시오 — 먼저 보낸 요청이 접수됐는지 알 수 없어 두 번 갈 수 있습니다.
- 결과를 내 쪽 기록과 맞춰야 하는 메시지는 대량 발송을 1건으로 쓰는 편이 낫습니다. 대량 발송에는 항목 참조값(
clientRef)이 있습니다(단건과 대량 중 무엇을 쓰나).
요청
POST/api/v1/alimtalk
| 헤더 | 필수 여부 | 설명 |
Authorization | 필수 | Bearer {accessToken} |
Content-Type | 필수 | application/json |
Idempotency-Key | 필수 | 요청마다 새로 만든 고유 값. 영문·숫자·하이픈 1~64자(UUID 권장). 이 값이 메시지의 중복 방지 키로도 쓰입니다. |
X-Trace-Id | 선택 | 요청 추적 ID |
요청 바디
| 필드 | 타입 | 필수 여부 | 설명 |
recvNo | String | 필수 | 수신 휴대폰 번호
010·011·016~019로 시작하는 10~11자리- 하이픈은 있어도 됩니다(예:
010-1234-5678)
|
content | String | 필수 | 알림톡 본문(승인된 템플릿 원문)
#{변수} 자리는 variables로 채웁니다(치환 변수).- 치환한 뒤 1,300자 이하. 줄바꿈은
\n
|
callbackNo | String | 필수 | 발신번호. 하이픈 없이 숫자 8~20자리. 미리 등록된 번호여야 합니다. |
alimTalk | Object | 필수 | 알림톡 발송 정보. AlimTalkOptions 참조 |
alimTalk.senderKey | String | 필수 | 발신 프로필 키(40자) |
alimTalk.templateCode | String | 필수 | 카카오가 승인한 템플릿 코드(30자 이하) |
alimTalk.adFlag | String | 선택 | 광고성 여부 Y | N. 기본값 N |
alimTalk.buttons | List | 선택 | 버튼 최대 5개. AlimTalkButton 참조 |
alimTalk.scheduledAt | String | 선택 | 예약 발송 시각. 시간대가 붙은 ISO 8601(예: 2026-10-10T09:00:00+09:00)이고 지금보다 뒤, 지금부터 30일 안이어야 합니다. 생략하면 바로 보냅니다. 보내기 전까지 예약 취소로 취소할 수 있습니다. |
variables | Object | 조건부 | 치환값. 본문·버튼에 #{변수}가 있으면 필수입니다. 값은 문자열 또는 문자열 배열(치환 변수) |
요청 예시
curl -X POST 'https://{게이트웨이 주소}/api/v1/alimtalk' \
-H 'Authorization: Bearer {accessToken}' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 6f1c2b8e-4d3a-4f1e-9b2c-7a5d8e9f0a11' \
-d '{
"recvNo": "010-1234-5678",
"content": "#{고객명}님, 주문(#{orderNo})이 접수되었습니다.",
"callbackNo": "0212345678",
"alimTalk": {
"senderKey": "{발신 프로필 키 40자}",
"templateCode": "ORDER_ACCEPT_01",
"adFlag": "N",
"buttons": [
{ "name": "주문 조회", "type": "WL",
"urlMobile": "https://m.example.com/orders/#{orderNo}",
"urlPc": "https://www.example.com/orders/#{orderNo}" }
]
},
"variables": { "고객명": "홍길동", "orderNo": "A1001" }
}'
응답
응답 바디
| 필드 | 타입 | 설명 |
success | Boolean | 성공하면 true |
code | String | SEND-0000 |
message | String | 결과 메시지 |
data | Object | 접수 결과. SendResult 참조 |
traceId | String | 요청 추적 ID |
data.messageId를 저장해 두십시오. 발송 결과 단건 조회의 messageKey로 씁니다. 응답 헤더 X-Message-Key에도 messageId가 들어 있습니다.
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | SEND-0000 | 요청이 접수되었습니다. |
| 200 | SEND-0003 | 개발 키로 보낸 요청입니다. 모의로 접수했고 실제로 발송하지 않습니다(키 환경). |
| 400 | CMN-4001 | 입력값 오류. 필드 검증 오류는 data.fieldErrors[]에, 그 밖의 이유는 message에 담깁니다. |
| 400 | SEND-4002 | 수신번호·발신번호·발신 프로필 키·템플릿 코드·예약 시각이 올바르지 않거나(예약 시각이 지났거나 30일을 넘은 경우 포함), 같은 Idempotency-Key로 내용이 다른 요청을 보냈습니다. 이유는 message에 있습니다. |
| 400 | SEND-4003 | 본문 길이·버튼이 올바르지 않습니다. 이유는 message에 있습니다. |
| 400 | SEND-4001 | alimTalk가 없습니다. |
| 400 | SEND-4006 | 치환 변수를 채울 수 없습니다. message에 어느 변수인지 나옵니다. |
| 400 | RATE-4001 | Idempotency-Key가 없거나 형식이 틀렸습니다. |
| 400 | RATE-4002 | 같은 Idempotency-Key로 다른 요청을 보냈습니다. 새 요청에는 새 키를 씁니다. |
| 401 | CMN-4011, AUTH-4013 등 | 토큰 오류 |
| 403 | RATE-4031~RATE-4035 | 채널 권한이 없거나 고객사·프로젝트 이용이 정지됨 |
| 409 | RATE-4091, SEND-4007 | 같은 Idempotency-Key 요청이 처리 중. Retry-After 초만큼 기다린 뒤 같은 키로 다시 확인합니다 |
| 429 | RATE-4291, RATE-4292, RATE-4293 | 호출 한도·발송 쿼터·계약 초당 한도 초과 |
| 502 | SEND-5021 | 발송 채널 처리 중 오류 |
| 503 | SEND-5031 | 발송 채널을 잠시 쓸 수 없어 보내지 않았습니다. Retry-After 뒤 같은 키로 다시 보냅니다. |
| 503 | CMN-5031 | 일시 장애. 접수됐을 수도 있습니다(다시 보내기). |
| 504 | SEND-5041 | 발송 채널 응답 지연. 접수됐을 수도 있습니다. |
응답 예시
{
"success": true,
"code": "SEND-0000",
"message": "요청이 접수되었습니다.",
"data": {
"messageId": "100231",
"channel": "ALIM_TALK",
"status": "ACCEPTED",
"acceptedAt": "2026-10-02T14:30:05.123+09:00"
},
"traceId": "3f9c2a7b1d4e5f60"
}
필드 검증 오류의 예입니다.
{
"success": false,
"code": "CMN-4001",
"message": "입력값이 올바르지 않습니다.",
"data": {
"fieldErrors": [
{ "field": "alimTalk.senderKey", "message": "발신프로필키는 40자여야 합니다." }
]
},
"traceId": "3f9c2a7b1d4e5f60"
}
알림톡
알림톡 대량 발송
한 번에 최대 500명에게 알림톡을 보냅니다. 같은 템플릿을 여러 명에게 보낼 때는 공통 템플릿 블록(template)을 한 번만 싣고, 항목에는 수신자와 치환값만 담습니다.
참고
- 요청당 항목은 1~500건입니다. 더 많이 보내려면 500건씩 나눠 여러 번 호출합니다.
- 발송 쿼터를 항목 수만큼 차감합니다. 남은 쿼터가 항목 수보다 적으면 한 건도 보내지 않고 거절합니다. 성공하지 않은 항목(거절·실패·확인 안 됨)의 쿼터는 결과가 나온 뒤 돌아옵니다.
- 항목마다 접수·거절 결과가 따로 나옵니다. 일부만 거절돼도 HTTP는 200입니다.
- 응답의
batchKey로 이 요청에서 보낸 메시지의 결과를 목록 조회할 수 있습니다.
작성 방식
| 방식 | 쓰는 경우 | 항목에 담는 것 |
| 템플릿 방식(권장) | 모든 수신자에게 같은 템플릿을 보냅니다. 본문·버튼·발신 정보는 template에 한 번만 씁니다. | recvNo, itemIdempotencyKey, clientRef, variables |
| 항목별 방식 | 수신자마다 본문이나 템플릿이 다릅니다. template을 보내지 않습니다. | 위 필드 + content, callbackNo, alimTalk |
두 방식은 섞을 수 없습니다. template을 보내면서 항목에 content나 alimTalk을 넣으면 요청 전체를 거절합니다(400 CMN-4001).
요청
POST/api/v1/alimtalk/batches
단건 발송과 같습니다. Idempotency-Key는 요청(배치)마다 새로 만듭니다.
요청 바디
| 필드 | 타입 | 필수 여부 | 설명 |
template | Object | 조건부 | 공통 템플릿 블록. 템플릿 방식에서 필수. SendBatchTemplate 참조 |
template.senderKey | String | 조건부 | 발신 프로필 키(40자) |
template.callbackNo | String | 조건부 | 발신번호. 하이픈 없이 숫자 8~20자리 |
template.templateCode | String | 조건부 | 승인된 템플릿 코드(30자 이하) |
template.body | String | 조건부 | 본문(승인된 템플릿 원문). 단건 발송의 content에 해당합니다. |
template.adFlag | String | 선택 | Y | N. 기본값 N |
template.buttons | List | 선택 | 버튼 최대 5개. AlimTalkButton |
template.scheduledAt | String | 선택 | 예약 발송 시각. 배치 전체가 같은 시각에 나갑니다. 규칙은 단건의 alimTalk.scheduledAt과 같습니다(지금보다 뒤, 지금부터 30일 안). |
items | List | 필수 | 발송 항목 1~500건. SendBatchItem 참조 |
items[].recvNo | String | 필수 | 수신 휴대폰 번호(단건 발송과 같은 규칙) |
items[].itemIdempotencyKey | String | 필수 | 항목 멱등 키. 1~64자
- 같은 배치 안에서 겹치면 안 됩니다. 다른 요청과도 겹치지 않게 UUID나
{발송그룹ID}-{수신자ID}처럼 만듭니다.
- 같은 항목을 다시 보낼 때는 같은 값을 씁니다. 이미 접수된 항목은 새로 발송하지 않고 처음의
messageId를 돌려줍니다.
|
items[].clientRef | String | 선택 | 내 쪽 수신자 식별자(64자 이하). 해석하지 않고 결과 조회 응답에 그대로 돌려줍니다. 개인정보를 넣지 마십시오. |
items[].variables | Object | 조건부 | 이 수신자의 치환값(치환 변수) |
items[].content
items[].callbackNo
items[].alimTalk | - | 조건부 | 항목별 방식에서만 씁니다. 의미는 단건 발송과 같습니다. |
요청 예시 — 템플릿 방식
curl -X POST 'https://{게이트웨이 주소}/api/v1/alimtalk/batches' \
-H 'Authorization: Bearer {accessToken}' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 0c9e7d1a-5b2f-4a38-8e61-3f2d9c4b7a10' \
-d '{
"template": {
"senderKey": "{발신 프로필 키 40자}",
"callbackNo": "0212345678",
"templateCode": "SURVEY_START_01",
"adFlag": "N",
"body": "#{name}님, 『#{surveyName}』 설문이 열렸습니다.",
"buttons": [
{ "name": "설문 참여", "type": "WL",
"urlMobile": "https://m.example.com/surveys/#{surveyId}",
"urlPc": "https://www.example.com/surveys/#{surveyId}" }
]
},
"items": [
{ "recvNo": "01012345678", "itemIdempotencyKey": "talk7788-25780", "clientRef": "25780",
"variables": { "name": "홍길동", "surveyName": "도민 만족도 조사", "surveyId": "S1001" } },
{ "recvNo": "010-9876-5432", "itemIdempotencyKey": "talk7788-25781", "clientRef": "25781",
"variables": { "name": "김영희", "surveyName": "도민 만족도 조사", "surveyId": "S1001" } }
]
}'
응답
응답 바디
| 필드 | 타입 | 설명 |
success | Boolean | 요청 자체가 처리되면 true(일부 거절 포함) |
code | String | 모두 접수 SEND-0000, 1건 이상 거절 SEND-0002 |
message | String | 예: 배치 요청이 접수되었습니다., 1건 접수, 1건 거절되었습니다. |
data | Object | 배치 결과. SendBatchResult 참조 |
traceId | String | 요청 추적 ID |
응답 헤더 X-Batch-Key에도 batchKey가 들어 있습니다.
항목 결과를 읽는 법
results[]는 요청 항목과 같은 순서이고 index는 요청 items에서의 위치(0부터)입니다.
messageId가 없는 항목은 접수되지 않은 것으로 처리하십시오. 이유는 errorCode·errorMessage에 있습니다(항목 거절 코드).
- 거절된 항목만 고쳐서 새
Idempotency-Key로 다시 보내면 됩니다.
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | SEND-0000 | 모든 항목이 접수되었습니다. |
| 200 | SEND-0003 | 개발 키로 보낸 요청입니다. 모든 항목을 모의로 접수했고 실제로 발송하지 않습니다. |
| 200 | SEND-0002 | 1건 이상 거절되었습니다. 항목별 결과를 확인합니다. |
| 400 | CMN-4001 | 요청 전체 거절. 항목 500건 초과, 필드 검증 오류(data.fieldErrors[]), 템플릿 블록에 templateCode·body·senderKey·callbackNo 중 빠진 것이 있음, 두 방식을 섞음, 링크 버튼에 urlMobile이 없음 |
| 400 | SEND-4001 | 항목별 방식인데 항목에 alimTalk가 없습니다. |
| 400 | SEND-4006 | 치환 변수를 채울 수 없습니다. message에 항목 번호(items[n])와 변수 이름이 나옵니다. 한 항목이라도 실패하면 요청 전체를 거절합니다. |
| 400 | RATE-4001 | Idempotency-Key가 없거나 형식이 틀렸습니다. |
| 400 | RATE-4002 | 같은 Idempotency-Key로 다른 요청을 보냈습니다. 새 요청에는 새 키를 씁니다. |
| 401 / 403 | - | 토큰 오류 / 권한 없음·이용 정지(게이트웨이 오류) |
| 409 | RATE-4091, SEND-4007 | 같은 Idempotency-Key 요청이 처리 중. Retry-After 초만큼 기다린 뒤 같은 키로 다시 확인합니다 |
| 429 | RATE-4291, RATE-4292, RATE-4293 | 호출 한도·발송 쿼터·계약 초당 한도 초과 |
| 502 | SEND-5021 | 발송 채널 처리 중 오류 |
| 503 | SEND-5031 | 발송 채널을 잠시 쓸 수 없어 보내지 않았습니다. Retry-After 뒤 다시 보냅니다(아래 참조). |
| 503 | CMN-5031 | 일시 장애. 실제로는 접수됐을 수 있습니다(아래 참조). |
| 504 | SEND-5041 | 발송 채널 응답 지연. 접수됐을 수 있습니다. |
503·504를 받았을 때
새 Idempotency-Key로, 항목의 itemIdempotencyKey는 그대로 다시 보내십시오. 이미 접수된 항목은 새로 발송되지 않고 처음의 messageId를 돌려받습니다. 이렇게 돌려받은 항목은 새 batchKey로는 조회되지 않으니 messageId로 조회하십시오.
응답 예시
{
"success": true,
"code": "SEND-0000",
"message": "배치 요청이 접수되었습니다.",
"data": {
"batchKey": "9f86d081884c4d659a2feaa0c55ad015",
"channel": "ALIM_TALK",
"accepted": 2,
"rejected": 0,
"results": [
{ "index": 0, "messageId": "100246", "status": "ACCEPTED", "errorCode": null, "errorMessage": null },
{ "index": 1, "messageId": "100247", "status": "ACCEPTED", "errorCode": null, "errorMessage": null }
]
},
"traceId": "a1b2c3d4e5f60718"
}
일부가 거절된 경우입니다.
{
"success": true,
"code": "SEND-0002",
"message": "1건 접수, 1건 거절되었습니다.",
"data": {
"batchKey": "9f86d081884c4d659a2feaa0c55ad015",
"channel": "ALIM_TALK",
"accepted": 1,
"rejected": 1,
"results": [
{ "index": 0, "messageId": "100246", "status": "ACCEPTED", "errorCode": null, "errorMessage": null },
{ "index": 1, "messageId": null, "status": "REJECTED", "errorCode": "SEND-4002",
"errorMessage": "요청 필드(수신처·발신프로필·발신번호·템플릿·요청시각)가 올바르지 않습니다." }
]
},
"traceId": "a1b2c3d4e5f60718"
}
알림톡
알림톡 발송 결과 단건 조회
발송할 때 받은 메시지 키(messageId)로 알림톡 한 건의 발송 상태와 결과를 조회합니다.
참고
- 자기 API Key로 발송한 메시지만 조회됩니다. 다른 키로 보낸 메시지나 없는 메시지 키는 똑같이
404 CMN-4041로 응답합니다.
- 결과는 발송사 회신을 받는 대로 반영되며 반영 주기는 30초입니다. 30초보다 자주 조회해도 새 정보는 없습니다.
- 최종 상태는
SUCCESS·FAIL·CANCELLED 셋입니다. 그 밖의 상태는 아직 결과를 기다리는 중입니다(상태·결과 코드).
요청
GET/api/v1/alimtalk/messages/{messageKey}
Authorization: Bearer {accessToken}가 필요합니다. 자세한 내용은 공통 요청 헤더를 참조해 주십시오.
요청 경로 파라미터
| 필드 | 타입 | 필수 여부 | 설명 |
messageKey | String | 필수 | 메시지 키. 발송 응답의 messageId 값입니다(숫자 문자열, 최대 19자리). |
요청 예시
curl -X GET 'https://{게이트웨이 주소}/api/v1/alimtalk/messages/100245' \
-H 'Authorization: Bearer {accessToken}'
응답
응답 바디
| 필드 | 타입 | 설명 |
ok | Boolean | 성공하면 true |
data | Object | 메시지 정보. Message 참조 |
traceId | String | 요청 추적 ID. 문의할 때 함께 알려 주십시오. |
값이 없는 필드는 null로 오지 않고 키 자체가 빠집니다. 예를 들어 결과를 기다리는 메시지에는 resultCode·resultAt이 없습니다.
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | - | 조회 성공 |
| 401 | CMN-4011, AUTH-4013 등 | 토큰이 없거나 만료·위조됨(게이트웨이 오류) |
| 404 | CMN-4041 | 대상을 찾을 수 없습니다(없는 키, 다른 API Key의 메시지, 형식이 맞지 않는 키, 개발 키로 받은 mock- 키). |
| 429 | RATE-4290 | 조회 한도(API Key별 초당 60회) 초과. Retry-After 초 뒤에 다시 호출합니다. |
| 503 | CMN-5031, EXT_SEND_DB_001 | 일시 장애. 잠시 뒤 다시 호출합니다. |
응답 예시
{
"ok": true,
"data": {
"messageKey": "100245",
"status": "SUCCESS",
"resultCode": "SUCCESS",
"resultAt": "2026-10-02T14:07:42+09:00",
"recvNoMask": "010-****-5678",
"clientRef": "12345",
"templateCode": "SURVEY_START_01",
"telecom": "SKT",
"vendorResultCode": "100",
"vendorResultCodeLabel": "SJ-100",
"resultMessage": "정상 발송되었습니다."
},
"traceId": "9f1c3a7e5b2d4c60"
}
오류일 때는 data 대신 error가 옵니다.
{
"ok": false,
"error": { "code": "CMN-4041", "message": "대상을 찾을 수 없습니다." },
"traceId": "9f1c3a7e5b2d4c60"
}
알림톡
알림톡 발송 결과 목록 조회
조건에 맞는 메시지 목록을 조회합니다. 대량 발송 응답의 batchKey로 한 번에 보낸 메시지의 결과를 모아 볼 수 있습니다.
참고
- 조회 기간은 접수 시각 기준입니다.
from·to를 생략하면 최근 31일을 조회하고, 한 번에 최대 92일까지 조회할 수 있습니다.
batchKey를 지정해도 기간 조건은 함께 걸립니다. 31일보다 오래된 배치를 조회하려면 from·to를 지정해 주십시오.
- 자기 API Key로 발송한 메시지만 결과에 포함됩니다.
요청
GET/api/v1/alimtalk/messages
Authorization: Bearer {accessToken}가 필요합니다. 자세한 내용은 공통 요청 헤더를 참조해 주십시오.
요청 쿼리 파라미터
| 필드 | 타입 | 필수 여부 | 설명 |
from | String | 선택 | 조회 시작 일시(접수 시각 기준, 포함)
- 형식:
yyyy-MM-dd 또는 시간대가 붙은 ISO 8601 일시(예: 2026-10-02T09:00:00+09:00)
- 날짜만 쓰면 그날 00:00:00(KST)
- 기본값:
to의 31일 전
|
to | String | 선택 | 조회 종료 일시(포함)
- 형식은
from과 같습니다. 날짜만 쓰면 그날 23:59:59.999(KST)
- 기본값: 현재 시각
from보다 앞설 수 없고, 기간은 최대 92일
|
statusCd | String | 선택 | 상태 하나로 거릅니다. ACCEPTED | SENDING | SENT | SUCCESS | FAIL | CANCELLED (대소문자 구분, 상태 코드) |
resultCd | String | 선택 | 결과 코드 하나로 거릅니다. 결과 코드 11종 중 하나 |
batchKey | String | 선택 | 대량 발송 응답의 batchKey. 32자 이하, 영문·숫자·- |
page | Integer | 선택 | 조회할 쪽 번호. 0부터 시작합니다. 기본값 0 |
size | Integer | 선택 | 쪽당 건수. 1~100, 기본값 20 |
sort | String | 선택 | 정렬 기준. 필드,방향 형식이며 두 번까지 반복할 수 있습니다.
- 필드:
reqDtm(접수 시각) | resultAt(결과 시각) | status(상태)
- 방향:
asc | desc — 생략할 수 없습니다
- 기본값:
reqDtm,desc
|
요청 예시
curl -G 'https://{게이트웨이 주소}/api/v1/alimtalk/messages' \
-H 'Authorization: Bearer {accessToken}' \
--data-urlencode 'batchKey=0a085cc5a74e4e38899797a936a369d6' \
--data-urlencode 'size=100'
응답
응답 바디
| 필드 | 타입 | 설명 |
ok | Boolean | 성공하면 true |
data.content | List | 메시지 목록. Message 참조 |
data.page.number | Integer | 현재 쪽 번호(0부터) |
data.page.size | Integer | 쪽당 건수 |
data.page.totalPages | Integer | 전체 쪽 수 |
data.page.numberOfElements | Integer | 이번 쪽의 건수 |
data.page.first | Boolean | 첫 쪽 여부 |
data.page.last | Boolean | 마지막 쪽 여부 |
data.totalElements | Long | 조건에 맞는 전체 건수 |
traceId | String | 요청 추적 ID |
결과가 없으면 content는 빈 목록이고 totalPages는 0입니다. 마지막 쪽을 넘는 page를 요청해도 오류 없이 빈 목록을 돌려줍니다.
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | - | 조회 성공 |
| 400 | CMN-4000 | 파라미터가 올바르지 않습니다. error.message에 이유가 들어 있습니다(예: 조회 기간은 최대 92일까지 허용됩니다.). 여러 개가 틀려도 첫 번째 이유 하나만 알려 줍니다. |
| 401 | CMN-4011, AUTH-4013 등 | 토큰이 없거나 만료·위조됨 |
| 429 | RATE-4290 | 조회 한도(API Key별 초당 60회, 단건 조회와 합산) 초과. Retry-After 초 뒤에 다시 호출합니다. |
| 503 | CMN-5031, EXT_SEND_DB_001 | 일시 장애. 잠시 뒤 다시 호출합니다. |
응답 예시
{
"ok": true,
"data": {
"content": [
{
"messageKey": "100247",
"status": "ACCEPTED",
"recvNoMask": "010-****-9012",
"clientRef": "12347",
"templateCode": "SURVEY_START_01"
},
{
"messageKey": "100246",
"status": "SENT",
"resultCode": "UNCERTAIN",
"resultAt": "2026-10-02T14:07:45+09:00",
"recvNoMask": "010-****-1234",
"clientRef": "12346",
"templateCode": "SURVEY_START_01",
"vendorResultCode": "7109",
"vendorResultCodeLabel": "KKO-7109",
"resultMessage": "발송사가 아직 결과를 확정하지 못했습니다. 잠시 뒤 다시 확인하세요. 카카오가 수신 확인을 회신하지 않았습니다."
},
{
"messageKey": "100244",
"status": "FAIL",
"resultCode": "FAIL_TEMPLATE",
"resultAt": "2026-10-02T14:07:41+09:00",
"recvNoMask": "010-****-3456",
"clientRef": "12344",
"templateCode": "SURVEY_START_01",
"vendorResultCode": "3016",
"vendorResultCodeLabel": "KKO-3016",
"resultMessage": "알림톡 템플릿이 승인되지 않았거나 내용과 맞지 않습니다. 템플릿 심사 상태를 확인하세요."
}
],
"page": {
"number": 0,
"size": 100,
"totalPages": 1,
"numberOfElements": 3,
"first": true,
"last": true
},
"totalElements": 3
},
"traceId": "9f1c3a7e5b2d4c60"
}
대량 발송 결과를 모으는 방법
- 대량 발송 응답의
batchKey와, 항목마다 넣은 clientRef를 저장해 둡니다. 결과는 clientRef로 내 쪽 기록과 맞춥니다(마스킹된 번호는 겹칠 수 있습니다).
batchKey로 목록을 조회하고 last가 true가 될 때까지 쪽을 넘깁니다(size=100 권장).
status가 SUCCESS·FAIL·CANCELLED인 건은 확정입니다. 나머지는 다음 주기에 다시 조회합니다.
- 모든 건이 확정되면 그 배치의 조회를 끝냅니다.
주의
- 한 배치의 메시지는 접수 시각이 같지만 쪽 사이의 순서는 고정됩니다. 다만 조회하는 동안 결과가 확정되면 정렬 값이 바뀌어 건이 다른 쪽으로 옮겨 갈 수 있습니다(상태순으로 정렬한 경우). 확정되지 않은 건이 남아 있으면 처음 쪽부터 다시 조회하십시오.
statusCd는 값 하나만 받습니다. 결과를 기다리는 건만 보려면 statusCd=ACCEPTED와 statusCd=SENT를 각각 조회합니다.
- 운영자가 실패 건을 다시 보내면 같은
clientRef로 새 메시지가 생기고, 그 메시지는 원래 batchKey로는 조회되지 않습니다(운영자 재발송).
알림톡
알림톡 예약 취소
발송할 때 받은 메시지 키(messageId)로 아직 나가지 않은 알림톡 한 건을 취소합니다.
참고
- 발송 시스템이 아직 가져가지 않은 메시지만 취소됩니다. 예약 시각이 되어 나가는 중이거나 이미 나간 메시지는
409 SEND-4004입니다.
- 예약하지 않은 메시지도 발송 시스템이 가져가기 전이면 취소되지만, 보통 몇 초 안에 가져갑니다. 취소는 예약 발송에 쓰십시오.
- 이미 취소한 메시지를 다시 취소해도 같은 답(
200, 처음 취소한 시각)이 옵니다. 응답을 못 받았으면 그대로 다시 호출하면 됩니다.
- 자기 API Key로 보낸 메시지만 취소됩니다. 다른 키로 보낸 메시지와 없는 메시지 키는 똑같이
404 CMN-4041입니다.
- 대량 발송의 한 항목도 그 항목의
messageId로 취소할 수 있습니다. 취소한 메시지는 결과 조회에서 CANCELLED로 보입니다.
- 취소한 건의 발송 쿼터는 돌아옵니다(취소 뒤 몇 분 안, 호출 한도).
요청
DELETE/api/v1/alimtalk/messages/{messageKey}
Authorization: Bearer {accessToken}가 필요합니다(공통 요청 헤더). Idempotency-Key는 쓰지 않습니다 — 취소는 몇 번 불러도 결과가 같습니다.
요청 경로 파라미터
| 필드 | 타입 | 필수 여부 | 설명 |
messageKey | String | 필수 | 메시지 키. 발송 응답의 messageId 값입니다(숫자 문자열). |
요청 예시
curl -X DELETE 'https://{게이트웨이 주소}/api/v1/alimtalk/messages/100245' \
-H 'Authorization: Bearer {accessToken}'
응답
형식 B로 옵니다.
응답 바디
| 필드 | 타입 | 설명 |
ok | Boolean | 취소했으면(이미 취소한 메시지 포함) true |
data.messageKey | String | 취소한 메시지 키 |
data.status | String | 늘 CANCELLED |
data.cancelledAt | String | 취소한 시각(ISO 8601, +09:00). 다시 취소하면 처음 취소한 시각입니다. |
data.traceId / traceId | String | 요청 추적 ID(둘이 같은 값) |
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | - | 취소했습니다(이미 취소한 메시지 포함). |
| 401 | CMN-4011, AUTH-4013 등 | 토큰이 없거나 만료·위조됨(게이트웨이 오류) |
| 404 | CMN-4041 | 대상을 찾을 수 없습니다(없는 키, 다른 API Key의 메시지, 형식이 맞지 않는 키, 개발 키로 받은 mock- 키). |
| 409 | SEND-4004 | 이미 나갔거나 나가는 중이라 취소할 수 없습니다. error.currentStatus에 지금 상태(SENDING·SENT·SUCCESS·FAIL 등)가 옵니다. |
| 429 | RATE-4290 | 취소 한도(API Key별 초당 60회) 초과. Retry-After 초 뒤에 다시 호출합니다. |
| 503 | CMN-5031, EXT_SEND_DB_001 | 일시 장애. 잠시 뒤 다시 호출합니다 — 취소는 다시 불러도 안전합니다. |
응답 예시
{
"ok": true,
"data": {
"messageKey": "100245",
"status": "CANCELLED",
"cancelledAt": "2026-10-19T14:20:33.123+09:00",
"traceId": "9f1c3a7e5b2d4c60"
},
"traceId": "9f1c3a7e5b2d4c60"
}
이미 나가는 중인 메시지를 취소하려 한 경우의 예입니다.
{
"ok": false,
"error": {
"code": "SEND-4004",
"message": "이미 처리되어 취소할 수 없다",
"currentStatus": "SENDING",
"currentStatCd": "1"
},
"traceId": "9f1c3a7e5b2d4c60"
}
알림톡
알림톡 대량 예약 취소
대량 발송 응답의 batchKey로 그 요청에서 아직 나가지 않은 항목을 한꺼번에 취소합니다.
참고
- 발송 시스템이 아직 가져가지 않은 항목만 취소됩니다. 이미 나갔거나 나가는 중인 항목은
skippedCount로 세고 그대로 둡니다. 일부만 취소돼도 200입니다.
- 앞서 취소한 항목은 어느 수에도 들지 않습니다 — 다시 부르면
cancelledCount가 0입니다.
- 어느 항목이 취소됐는지는 결과 목록 조회(
batchKey)에서 CANCELLED로 확인합니다.
- 응답을 못 받아 새
Idempotency-Key로 다시 보낸 대량 발송은 batchKey가 다릅니다. 앞 요청에서 접수된 항목은 앞 요청의 batchKey나 항목의 messageId(예약 취소)로 취소합니다.
- 자기 API Key로 보낸 대량 발송만 취소됩니다(다른 키·없는 키는
404 CMN-4041). 취소한 건의 쿼터는 돌아옵니다.
요청
DELETE/api/v1/alimtalk/messages/batches/{batchKey}
예약 취소와 같습니다.
요청 경로 파라미터
| 필드 | 타입 | 필수 여부 | 설명 |
batchKey | String | 필수 | 대량 발송 응답의 data.batchKey(응답 헤더 X-Batch-Key와 같다) |
요청 예시
curl -X DELETE 'https://{게이트웨이 주소}/api/v1/alimtalk/messages/batches/b1f0a2c4111122223333444455556666' \
-H 'Authorization: Bearer {accessToken}'
응답
형식 B로 옵니다.
응답 바디
| 필드 | 타입 | 설명 |
ok | Boolean | 처리했으면(일부만 취소한 경우 포함) true |
data.batchKey | String | 취소한 대량 발송의 batchKey |
data.cancelledCount | Integer | 이번에 취소한 항목 수 |
data.skippedCount | Integer | 취소하지 못한 항목 수 — 이미 나갔거나 나가는 중 |
data.status | String | 늘 CANCELLED |
data.cancelledAt | String | 처리한 시각(ISO 8601, +09:00) |
data.traceId / traceId | String | 요청 추적 ID(둘이 같은 값) |
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | - | 처리했습니다. 수는 cancelledCount·skippedCount에 있습니다. |
| 401 | CMN-4011, AUTH-4013 등 | 토큰이 없거나 만료·위조됨(게이트웨이 오류) |
| 404 | CMN-4041 | 대상을 찾을 수 없습니다(없는 batchKey, 다른 API Key의 대량 발송, 개발 키로 받은 mock- 키). |
| 429 | RATE-4290 | 취소 한도(API Key별 초당 60회, 단건 취소와 합산) 초과. Retry-After 초 뒤에 다시 호출합니다. |
| 503 | CMN-5031, EXT_SEND_DB_001 | 일시 장애. 잠시 뒤 다시 호출합니다 — 다시 불러도 안전합니다. |
응답 예시
{
"ok": true,
"data": {
"batchKey": "b1f0a2c4111122223333444455556666",
"cancelledCount": 8,
"skippedCount": 2,
"status": "CANCELLED",
"cancelledAt": "2026-10-19T14:25:10.456+09:00",
"traceId": "9f1c3a7e5b2d4c60"
},
"traceId": "9f1c3a7e5b2d4c60"
}
알림톡
알림톡 데이터 타입
요청과 응답에 쓰이는 객체의 필드를 정리했습니다.
AlimTalkOptions
단건 발송과 항목별 방식 대량 발송의 alimTalk 필드입니다.
| 필드 | 타입 | 필수 여부 | 설명 |
senderKey | String | 필수 | 발신 프로필 키. 정확히 40자 |
templateCode | String | 필수 | 카카오가 승인한 템플릿 코드. 30자 이하 |
adFlag | String | 선택 | 광고성 여부 Y | N. 생략하면 N. 빈 문자열은 오류입니다. |
buttons | List<AlimTalkButton> | 선택 | 최대 5개 |
scheduledAt | String | 선택 | 예약 발송 시각. 시간대가 붙은 ISO 8601(예: 2026-10-10T09:00:00+09:00). 지금보다 뒤, 지금부터 30일 안이어야 합니다. |
| 필드 | 타입 | 필수 여부 | 설명 |
name | String | 필수 | 버튼 이름. 14자 이하. 승인된 템플릿의 버튼 이름과 같아야 합니다. |
type | String | 필수 | 버튼 종류(아래 표) |
urlMobile | String | 조건부 | 모바일 링크. WL·AL이면 필수 |
urlPc | String | 선택 | PC 링크 |
| type | 뜻 |
WL | 웹 링크 |
AL | 앱 링크 |
BK | 봇 키워드 |
MD | 메시지 전달 |
DS | 배송 조회 |
BC | 상담톡 전환 |
BT | 봇 전환 |
AC | 채널 추가 |
name·urlMobile·urlPc에도 #{변수}를 쓸 수 있습니다.
SendBatchTemplate
대량 발송 템플릿 방식의 template 필드입니다. templateCode·body·senderKey·callbackNo는 넷 다 있어야 합니다.
| 필드 | 타입 | 필수 여부 | 설명 |
senderKey | String | 필수 | 발신 프로필 키(40자) |
callbackNo | String | 필수 | 발신번호. 하이픈 없이 숫자 8~20자리 |
templateCode | String | 필수 | 승인된 템플릿 코드(30자 이하) |
body | String | 필수 | 본문(승인된 템플릿 원문). 치환한 뒤 1,300자 이하 |
adFlag | String | 선택 | Y | N. 기본값 N |
buttons | List<AlimTalkButton> | 선택 | 최대 5개 |
scheduledAt | String | 선택 | 예약 발송 시각. 배치 전체에 적용(지금보다 뒤, 지금부터 30일 안) |
SendBatchItem
| 필드 | 타입 | 필수 여부 | 설명 |
recvNo | String | 필수 | 수신 휴대폰 번호(010·011·016~019로 시작하는 10~11자리, 하이픈 허용) |
itemIdempotencyKey | String | 필수 | 항목 멱등 키. 1~64자. 같은 배치 안에서 겹치면 안 됩니다. |
clientRef | String | 선택 | 내 쪽 수신자 식별자. 64자 이하. 개인정보 금지 |
variables | Object | 조건부 | 치환값(치환 변수) |
content | String | 조건부 | 항목별 방식에서 필수. 템플릿 방식에서는 넣지 않습니다. |
callbackNo | String | 조건부 | 항목별 방식에서 필수. 템플릿 방식에서는 무시되고 template.callbackNo를 씁니다. |
alimTalk | AlimTalkOptions | 조건부 | 항목별 방식에서 필수. 템플릿 방식에서는 넣지 않습니다. |
SendResult
단건 발송 응답의 data입니다.
| 필드 | 타입 | 설명 |
messageId | String | 메시지 키. 결과 조회의 messageKey로 씁니다. |
channel | String | ALIM_TALK |
status | String | ACCEPTED |
acceptedAt | String | 접수 시각(ISO 8601, +09:00) |
SendBatchResult
대량 발송 응답의 data입니다.
| 필드 | 타입 | 설명 |
batchKey | String | 배치 키(소문자 16진수 32자). 결과 목록 조회의 batchKey로 씁니다. |
channel | String | ALIM_TALK |
accepted | Integer | 접수된 항목 수 |
rejected | Integer | 거절된 항목 수 |
results | List | 항목별 결과(요청 순서) |
results[].index | Integer | 요청 items에서의 위치(0부터) |
results[].messageId | String | 메시지 키. 거절된 항목은 null |
results[].status | String | ACCEPTED | REJECTED |
results[].errorCode | String | 거절 코드(항목 거절 코드). 접수된 항목은 null |
results[].errorMessage | String | 거절 이유. 접수된 항목은 null |
Message
발송 결과 조회 응답의 메시지 한 건입니다. 값이 없는 필드는 키 자체가 빠집니다.
| 필드 | 타입 | 설명 |
messageKey | String | 메시지 키(발송 응답의 messageId) |
status | String | 상태 코드(상태 코드). 항상 있습니다. |
resultCode | String | 결과 코드(결과 코드). 결과가 나오기 전에는 없습니다. |
resultAt | String | 결과를 받은 시각(ISO 8601, +09:00) |
recvNoMask | String | 가린 수신번호(예: 010-****-5678) |
clientRef | String | 대량 발송 때 넣은 참조값. 단건 발송 메시지에는 없습니다. |
templateCode | String | 템플릿 코드 |
telecom | String | 수신자 통신사(발송사가 알려 준 경우) |
vendorResultCode | String | 발송사 원본 결과 코드 |
vendorResultCodeLabel | String | 출처를 붙인 원본 코드(예: SJ-100, KKO-3016) |
resultMessage | String | 결과 설명 문구 |
치환 변수 (variables)
본문과 버튼의 #{이름} 자리를 variables 값으로 채워서 보냅니다.
규칙
- 이름: 한글·영문·숫자·밑줄(
_). 공백·하이픈·마침표는 쓸 수 없고, 대소문자를 구분합니다.
- 적용 위치: 본문(
content, template.body)과 버튼의 name·urlMobile·urlPc. 발신 프로필 키·템플릿 코드·발신번호에는 적용되지 않습니다.
- 문자열 값: 같은 이름의 모든 자리에 같은 값이 들어갑니다.
- 문자열 배열 값: 자리마다 순서대로 하나씩 들어갑니다. 본문에 나오는 순서로 먼저 쓰고, 그다음 버튼 순서대로 버튼 하나당 한 칸을 씁니다(한 버튼의
name·urlMobile·urlPc는 같은 칸을 씁니다).
- 배열 길이는 실제로 쓰인 자리 수와 정확히 같아야 합니다. 어디에도 쓰이지 않은 배열 변수도 오류입니다. 쓰이지 않은 문자열 변수는 무시합니다.
- 숫자·참/거짓·객체는 값으로 쓸 수 없습니다. 숫자도
"2"처럼 문자열로 보냅니다.
- 값이 없거나
null이거나 빈 배열이면 오류입니다. variables를 보내지 않아도 본문에 #{…}가 남아 있으면 오류입니다.
- 본문에 글자 그대로의
#{를 쓸 수는 없습니다. 값 안에 든 #{는 그대로 들어갑니다.
배열 예시
{
"content": "#{고객명}님, 1. #{surveyName} 2. #{surveyName}",
"alimTalk": {
"buttons": [
{ "name": "첫 번째 설문", "type": "WL", "urlMobile": "https://example.com/s/#{surveyId}?t=#{token}" },
{ "name": "두 번째 설문", "type": "WL", "urlMobile": "https://example.com/s/#{surveyId}?t=#{token}" }
]
},
"variables": {
"고객명": "홍길동",
"surveyName": ["설문A", "설문B"],
"surveyId": ["S-1", "S-2"],
"token": ["t1", "t2"]
}
}
결과: 본문은 홍길동님, 1. 설문A 2. 설문B, 버튼 링크는 …/s/S-1?t=t1, …/s/S-2?t=t2가 됩니다(요청의 다른 필드는 줄였습니다).
오류 메시지
치환에 실패하면 요청 전체를 400 SEND-4006으로 거절하고, message에 이유를 적습니다. 자리 이름은 본문, 버튼[i].name, 버튼[i].urlMobile, 버튼[i].urlPc이고, 대량 발송에서는 앞에 items[n].이 붙습니다.
| 상황 | message 형식 |
| 값이 없음 | {자리} 의 치환값이 없습니다: {이름}, … |
표기가 틀림(#{이름, #{a-b} 등) | {자리} 의 변수 표기가 올바르지 않습니다. #{변수명} 형식인지 확인하세요. |
| 배열 값이 모자람 | {자리} 의 {이름} 에 꽂을 값이 모자랍니다(…) |
| 배열에 문자열이 아닌 값 | {자리} 의 {이름} 배열에 글자가 아닌 값이 있습니다 |
| 값이 문자열·문자열 배열이 아님 | {자리} 의 {이름} 은 글자 또는 글자 배열이어야 합니다 |
| 배열 값이 남거나 쓰이지 않음 | 값의 개수와 치환 자리 수가 다릅니다: {이름}(보낸 값 n개, 쓰인 자리 m곳) |
알림톡
알림톡 상태·결과 코드
조회 응답의 status는 메시지가 어디까지 왔는지, resultCode는 최종 결과가 무엇인지 알려 줍니다.
상태 코드 (status)
| 값 | 의미 | 최종 여부 | 할 일 |
ACCEPTED | 접수되어 결과를 기다리는 중 | 아니오 | 다음 주기에 다시 조회 |
SENT | 발송사가 아직 결과를 확정하지 못함. resultCode는 UNCERTAIN | 아니오 | 다시 보내지 말고 계속 조회. 약 5분 안에 확정되지 않으면 FAIL(FAIL_TIME)로 바뀝니다. |
SUCCESS | 발송 성공 | 예 | - |
FAIL | 발송 실패. 이유는 resultCode | 예 | 결과 코드별 안내를 따릅니다. |
CANCELLED | 발송 전에 취소됨 — 예약 취소 API, 운영자 취소, 고객사 이용 정지·해지에 따른 자동 취소(운영자 예약 취소) | 예 | - |
SENDING은 목록 조회의 statusCd 필터 값으로 받지만, 지금은 이 상태로 남는 메시지가 없습니다.
참고
결과가 오지 않아 ACCEPTED에 오래 머무는 메시지가 있으면 messageKey와 발송 때 받은 traceId를 담당자에게 알려 주십시오.
결과 코드 (resultCode)
resultMessage에는 아래 문구가 그대로 들어갑니다. 내 화면에 사유를 보여 줄 때 그대로 써도 됩니다.
| resultCode | status | resultMessage | 다시 보내도 되나 |
SUCCESS | SUCCESS | 정상 발송되었습니다. | 필요 없음 |
FAIL_RECIPIENT | FAIL | 수신번호가 올바르지 않습니다. 번호를 확인한 뒤 다시 발송하세요. | 번호를 고친 뒤에만 |
FAIL_SENDER_PROFILE | FAIL | 발신 프로필 또는 발신번호가 등록되어 있지 않습니다. 발송 설정을 확인하세요(수신자 문제가 아닙니다). | 발신 설정을 고친 뒤에 |
FAIL_TEMPLATE | FAIL | 알림톡 템플릿이 승인되지 않았거나 내용과 맞지 않습니다. 템플릿 심사 상태를 확인하세요. 원본 코드에 따라 문구가 다릅니다(아래). | 템플릿·본문·버튼을 고친 뒤에 |
FAIL_CONTENT_LENGTH | FAIL | 본문 길이 또는 형식이 규격을 벗어났습니다. 내용을 줄이거나 형식을 맞추세요. | 본문을 고친 뒤에 |
UNDELIVERED | FAIL | 발송사까지는 정상 처리되었으나 단말에 도달하지 못했습니다. | 내 정책에 따라 |
FAIL_BLOCKED | FAIL | 수신자가 알림톡을 받을 수 없는 상태입니다(수신 차단 또는 카카오 미사용). 재발송해도 같은 결과입니다. | 아니오 |
FAIL_VENDOR | FAIL | 발송사 시스템 오류로 실패했습니다. 잠시 후 재발송하세요. | 잠시 뒤 가능 |
FAIL_TIME | FAIL | 발송은 접수되었으나 정해진 시간 안에 발송사로부터 결과를 확인하지 못했습니다. 메시지가 실제로 도착했을 수 있으므로, 재발송 전에 수신 여부를 확인하세요. | 아래 FAIL_TIME 참조 |
FAIL_QUOTA | FAIL | 발송 한도를 넘었거나 스팸으로 차단되었습니다. 한도 회복 후 재발송하세요. | 한도가 회복된 뒤에 |
UNCERTAIN | SENT | 발송사가 아직 결과를 확정하지 못했습니다. 잠시 뒤 다시 확인하세요. | 아니오 — 계속 조회 |
FAIL_TIME 구분
FAIL_TIME에는 성격이 다른 두 경우가 함께 들어 있습니다. vendorResultCode로 나눠 처리해 주십시오.
| vendorResultCode | 뜻 | 할 일 |
901, 3005, 7109 | 결과 확인 시간이 지나 실패로 정리된 건. 실제로는 도착했을 수 있습니다. | 수신 여부를 확인하기 전에는 다시 보내지 않습니다. |
900 | 요청 시각이 올바르지 않아 발송되지 않은 건 | 요청 시각을 확인한 뒤 다시 보냅니다. |
901·3005·7109에는 resultMessage 끝에 다음 문장이 붙습니다.
- 901:
발송사 결과 대기 시간이 초과되었습니다(세종 E_REP_TIMEOUT).
- 3005, 7109:
카카오가 수신 확인을 회신하지 않았습니다.
FAIL_TEMPLATE 구분
FAIL_TEMPLATE은 고칠 곳에 따라 resultMessage 문구가 다릅니다.
| vendorResultCode | resultMessage |
3015 | 이 발신 프로필에서 템플릿을 찾을 수 없습니다. 템플릿 코드와 발신 프로필 키가 짝이 맞는지, 템플릿이 승인되어 사용 중인지 확인하세요. |
3016, 7104 | 본문이 승인된 템플릿과 다릅니다. 변수 자리 밖의 글자·줄바꿈·공백까지 템플릿과 같은지 확인하세요. |
3027 | 버튼이 승인된 템플릿과 다릅니다. 버튼 이름·종류·링크가 템플릿에 등록된 것과 같은지 확인하세요. |
그 밖(920, 925, 926, 944) | 알림톡 템플릿이 승인되지 않았거나 내용과 맞지 않습니다. 템플릿 심사 상태를 확인하세요. |
발송사 원본 코드
vendorResultCode는 발송사가 돌려준 원본 코드이고, vendorResultCodeLabel은 출처를 붙인 표기입니다.
| 표기 | 출처 | 예 |
SJ- + 3자리 | 발송 중계사 결과 코드 | SJ-100(성공), SJ-209(수신번호 오류) |
KKO- + 4자리 | 카카오 결과 코드 | KKO-3016(템플릿 불일치), KKO-3019(수신 차단) |
문의할 때는 resultCode와 함께 vendorResultCodeLabel을 알려 주시면 원인을 빨리 찾을 수 있습니다.
알림톡
알림톡 오류 코드
알림톡에서만 나오는 코드입니다. 인증·호출 한도·발송 공통 코드는 공통 오류 코드에 있습니다.
알림톡 발송 (형식 A)
| 코드 | HTTP | 메시지 | 대처 |
SEND-4001 | 400 | 알림톡 발송 정보(alimTalk)가 필요합니다. | alimTalk를 넣습니다. |
CMN-4001 | 400 | 링크 버튼에는 모바일 URL 이 필요합니다. | WL·AL 버튼에 urlMobile을 넣습니다. |
CMN-4001 | 400 | 템플릿 블록에는 templateCode·body·senderKey·callbackNo 가 모두 있어야 합니다. | 대량 발송의 template을 채웁니다. |
CMN-4001 | 400 | items[n] — 템플릿 블록을 쓰는 배치에는 항목에 content·alimTalk·sms 를 담을 수 없습니다. | 템플릿 방식에서는 항목에 content·alimTalk를 넣지 않습니다. |
SEND-4002 | 400 | 요청 필드(수신처·발신프로필·발신번호·템플릿·요청시각)가 올바르지 않습니다. | 수신번호·발신번호·발신 프로필 키·템플릿 코드·예약 시각을 확인합니다. 예약 시각이 지났으면 메시지가 "예약 시각은 지금보다 뒤여야 합니다.", 30일을 넘으면 "예약 시각은 지금부터 30일 안이어야 합니다." 입니다. 메시지가 "같은 Idempotency-Key 로 내용이 다른 요청을 보냈습니다" 이면 그 키로 먼저 보낸 요청과 내용이 다른 것입니다 — 새 요청에는 새 키를 씁니다(먼저 보낸 요청은 그대로입니다). 대량 발송에서는 같은 코드가 항목 거절 코드로 옵니다. |
SEND-4003 | 400 | 본문 길이 또는 형식이 올바르지 않습니다. | 치환한 본문 길이(1,300자 이하)와 버튼을 확인합니다. 대량 발송에서는 같은 코드가 항목 거절 코드로 옵니다. |
대량 발송 항목 거절 코드
대량 발송 응답 results[].errorCode에 담기는 코드입니다. 해당 항목만 접수되지 않았습니다.
| errorCode | errorMessage | 대처 |
SEND-4002 | 요청 필드(수신처·발신프로필·발신번호·템플릿·요청시각)가 올바르지 않습니다. | 수신번호, 발신 프로필 키, 발신번호, 템플릿 코드, 예약 시각(지금보다 뒤, 지금부터 30일 안), itemIdempotencyKey·clientRef 길이를 확인합니다. |
SEND-4002 | 같은 itemIdempotencyKey 로 내용이 다른 항목을 보냈습니다. 새 항목에는 새 키를 쓰세요. | 이 항목키로 먼저 보낸 항목과 내용(받는 번호·본문·버튼 등)이 다릅니다(먼저 보낸 항목은 그대로입니다). 새 항목이면 새 itemIdempotencyKey를 씁니다. |
SEND-4003 | 본문 길이 또는 형식이 올바르지 않습니다. | 치환 후 본문이 1,300자 이하인지, 버튼이 규격에 맞는지 확인합니다. |
EXT_SEND_DB_001 | 발송 시스템 일시 장애 — 잠시 후 재시도 | 잠시 뒤 같은 itemIdempotencyKey로 다시 보냅니다. |
EXT_SEND_DB_004 | 발송 접수 확인 실패 — 중복 발송 방지 후 재시도 | 같은 배치 안에 itemIdempotencyKey가 겹쳤는지 확인하고, 같은 키로 다시 보냅니다. |
발송 결과 조회 (형식 B)
| 코드 | HTTP | 메시지 | 대처 |
CMN-4000 | 400 | 파라미터마다 다른 이유(예: 조회 기간은 최대 92일까지 허용됩니다.) | error.message를 보고 파라미터를 고칩니다. |
CMN-4041 | 404 | 대상을 찾을 수 없습니다. | 메시지 키를 확인합니다. 다른 API Key로 보낸 메시지는 조회되지 않습니다. |
RATE-4290 | 429 | 요청 한도를 초과했습니다. | Retry-After(1초) 뒤 다시 조회합니다. |
EXT_SEND_DB_001 | 503 | 발송 시스템 일시 장애 — 잠시 후 재시도 | 잠시 뒤 다시 조회합니다. |
CMN-5000 | 500 | 서버 내부 오류가 발생했습니다. | 잠시 뒤 다시 조회합니다. 계속되면 문의합니다. |
같은 CMN-4041이라도 게이트웨이(없는 경로)는 형식 A, 조회 API(없는 메시지)는 형식 B로 옵니다.
예약 취소 (형식 B)
| 코드 | HTTP | 메시지 | 대처 |
CMN-4041 | 404 | 대상을 찾을 수 없습니다. | 메시지 키·batchKey를 확인합니다. 다른 API Key로 보낸 메시지는 취소되지 않습니다. |
SEND-4004 | 409 | 이미 처리되어 취소할 수 없다 | 이미 나갔거나 나가는 중입니다(error.currentStatus에 지금 상태). 결과는 조회로 확인합니다. |
RATE-4290 | 429 | 요청 한도를 초과했습니다. | Retry-After(1초) 뒤 다시 취소합니다. |
EXT_SEND_DB_001 | 503 | 발송 시스템 일시 장애 — 잠시 후 재시도 | 잠시 뒤 다시 취소합니다. 취소는 다시 불러도 안전합니다. |
CMN-5000 | 500 | 서버 내부 오류가 발생했습니다. | 잠시 뒤 다시 취소합니다. 계속되면 문의합니다. |
알림톡
알림톡 주의 사항
알림톡을 연동할 때 자주 놓치는 것들입니다. 모든 채널에 같은 규칙은 멱등 키·다시 보내기와 요청·응답 규칙을 참조해 주십시오.
항목 멱등 키 (itemIdempotencyKey)
- 수신자 한 명에게 보내는 한 통을 가리킵니다. 1~64자이고, 같은 배치 안에서 겹치면 안 됩니다.
- 다른 요청과도 겹치지 않게 만듭니다(UUID, 또는
{발송그룹ID}-{수신자ID}처럼 내 쪽에서 유일한 값).
- 같은 항목을 다시 보낼 때는 같은 값을 씁니다. 이미 접수된 항목은 새로 발송하지 않고 처음의
messageId를 돌려줍니다.
응답을 못 받았을 때
500·502·503·504를 받았거나 응답이 오지 않았으면 접수됐는지 알 수 없습니다.
- 대량 발송 — 새
Idempotency-Key로, 항목의 itemIdempotencyKey는 그대로 다시 보냅니다. 이미 접수된 항목은 새로 발송되지 않고 처음의 messageId를 돌려받습니다. 이렇게 돌려받은 항목은 새 batchKey로는 조회되지 않으니 messageId로 조회합니다.
- 단건 발송 — 응답을 못 받았을 때는 같은
Idempotency-Key로 다시 보내십시오. 먼저 보낸 요청이 접수됐으면 처음의 messageId를 그대로 돌려받고, 접수되지 않았으면 그때 발송됩니다. 아직 처리 중이면 409(SEND-4007)와 Retry-After가 오니 그 초만큼 기다린 뒤 같은 키로 다시 확인하십시오. 새 키로 바꿔 보내지 마십시오 — 먼저 보낸 요청이 접수됐는지 알 수 없어 두 번 갈 수 있습니다.
그 밖의 오류는 다시 보내기 표를 따릅니다.
단건과 대량 중 무엇을 쓰나
| 단건 발송 | 대량 발송 |
| 한 번에 보내는 수 | 1명 | 1~500명 |
| 내 쪽 기록과 결과 맞추기 | messageId만 | clientRef, batchKey, messageId |
| 응답을 못 받았을 때 다시 보내기 | 같은 멱등 키로 | 항목 멱등 키로 |
| 쿼터 차감 | 1건 | 항목 수만큼 |
다시 보내기는 양쪽 모두 안전합니다. 여러 명에게 보내거나 결과를 내 쪽 기록(clientRef)과 맞춰야 하면 대량 발송을 쓰십시오 — 수신자가 한 명이어도 됩니다.
결과 조회
- 발송 응답은 "접수"까지만 알려 줍니다. 전달 결과는 목록 조회나 단건 조회로 가져갑니다.
- 결과 반영 주기가 30초이므로 1분 정도 간격으로 조회하면 충분합니다.
SUCCESS·FAIL·CANCELLED가 될 때까지 조회합니다. SENT(UNCERTAIN)는 확정이 아니니 다시 보내지 말고 기다립니다.
- 목록 조회는 기본 31일, 최대 92일 범위입니다. 오래된 배치는
from·to를 지정합니다.
예약 발송
scheduledAt을 넣으면 그 시각에 보냅니다. 지금보다 뒤, 지금부터 30일 안이어야 합니다(정확히 30일 뒤까지 받습니다).
- 응답을 못 받아 다시 보낼 때 예약 시각이 이미 지났다면 — 앞 요청이 접수됐으면 처음 접수 결과를 그대로 돌려받고, 접수되지 않았으면
400 SEND-4002(예약 시각은 지금보다 뒤여야 합니다)입니다. 그때는 새 시각과 새 키로 보냅니다.
- 보내기 전까지 예약 취소(메시지 키)나 대량 예약 취소(
batchKey)로 취소할 수 있습니다. 예약 시각이 되어 발송 시스템이 가져간 뒤에는 취소할 수 없습니다(409 SEND-4004).
- 예약 시각까지는
ACCEPTED 상태로 조회됩니다. 취소하면 CANCELLED입니다.
- 운영자도 콘솔에서 예약을 취소할 수 있고, 고객사 이용이 정지·해지되면 남은 예약은 자동으로 취소됩니다(운영자 예약 취소).
- 취소한 건의 발송 쿼터는 돌아옵니다(취소 뒤 몇 분 안).
참조값과 개인정보
clientRef에는 개인정보(이름·전화번호 등)를 넣지 마십시오. 내 쪽 일련번호 같은 값을 씁니다.
- 조회 응답의 수신번호는 가려서(
010-****-5678) 돌려줍니다. 결과는 clientRef나 messageId로 맞춥니다.
문자
문자 단건 발송
수신자 한 명에게 문자를 보냅니다. 본문 길이에 따라 단문(SMS)이나 장문(LMS)으로 나갑니다. 응답은 접수 결과입니다.
참고
- 발신번호는 이 API Key가 속한 프로젝트에 등록된 번호여야 합니다. 다른 프로젝트에 등록된 번호는 쓸 수 없습니다.
- 발송 쿼터를 1건 차감합니다. 성공하지 않으면 결과가 나온 뒤 돌아옵니다(개발 키는 차감하지 않고 발송하지도 않습니다 — 키 환경).
sms.scheduledAt을 넣으면 그 시각에 보냅니다(예약). 보내기 전까지 예약 취소로 취소할 수 있습니다(예약 발송과 취소).
- 전달 결과는 응답의
messageId로 결과 단건 조회에서 가져갑니다.
- 여러 명에게는 대량 발송을 씁니다. 제목과 광고 문자는 아직 없습니다(아직 안 되는 것).
요청
POST/api/v1/sms
| 헤더 | 필수 여부 | 설명 |
Authorization | 필수 | Bearer {accessToken} |
Content-Type | 필수 | application/json |
Idempotency-Key | 필수 | 요청마다 새로 만든 고유 값. 영문·숫자·하이픈 1~64자(UUID 권장). 응답을 못 받아 다시 보낼 때는 같은 값을 씁니다(다시 보내기). |
X-Trace-Id | 선택 | 요청 추적 ID |
요청 바디
| 필드 | 타입 | 필수 여부 | 설명 |
recvNo | String | 필수 | 수신 휴대폰 번호
010·011·016~019로 시작하는 10~11자리- 하이픈·공백은 있어도 됩니다(예:
010-1234-5678)
|
content | String | 필수 | 본문
- 치환한 뒤 80바이트 이하는 단문, 2,000바이트 이하는 장문으로 나갑니다. 2,000바이트를 넘으면 거절합니다(길이 세는 법).
- 이모지처럼 문자로 보낼 수 없는 글자(보낼 수 없는 글자)와, 줄바꿈·탭 밖의 제어문자는 거절합니다.
#{변수} 자리는 variables로 채웁니다. 줄바꿈은 \n
|
callbackNo | String | 필수 | 발신번호. 숫자 8~20자리이고 하이픈은 있어도 됩니다. 이 API Key의 프로젝트에 등록된 발신번호만 쓸 수 있습니다 — 아니면 400 SEND-4008입니다. |
variables | Object | 조건부 | 치환값. 본문에 #{변수}가 있으면 필수입니다. 규칙은 알림톡과 같습니다(치환 변수). |
sms | Object | 선택 | 문자 발송 옵션 |
sms.scheduledAt | String | 선택 | 예약 발송 시각
- 시간대가 붙은 ISO 8601(예:
2026-10-20T09:00:00+09:00)이고 지금보다 뒤, 지금부터 30일 안이어야 합니다. 초 아래는 버립니다.
- 시간대가 없는 값(예:
2026-10-20T09:00:00)은 읽지 못해 400 CMN-4001입니다.
- 생략하거나
null·빈 문자열이면 바로 보냅니다(예약 발송과 취소).
|
이 밖의 필드는 문자에서 쓰지 않으며 보내도 무시됩니다.
요청 예시
curl -X POST 'https://{게이트웨이 주소}/api/v1/sms' \
-H 'Authorization: Bearer {accessToken}' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 2b7e4f1a-9c3d-4e8b-a6f0-5d1c3b9e7a24' \
-d '{
"recvNo": "010-1234-5678",
"content": "[리노] #{name}님, 인증번호는 #{code}입니다.",
"callbackNo": "02-1234-5678",
"variables": { "name": "홍길동", "code": "482913" }
}'
예약 발송 — 10월 20일 오전 9시(한국 시간)에 보내는 요청 바디입니다.
{
"recvNo": "010-1234-5678",
"content": "[리노] 오늘 오전 10시에 설문이 열립니다.",
"callbackNo": "02-1234-5678",
"sms": { "scheduledAt": "2026-10-20T09:00:00+09:00" }
}
응답
응답 바디
| 필드 | 타입 | 설명 |
success | Boolean | 성공하면 true |
code | String | SEND-0000(개발 키는 SEND-0003) |
message | String | 결과 메시지 |
data.messageId | String | 메시지 키. 저장해 두십시오. |
data.channel | String | SMS |
data.status | String | ACCEPTED — 예약도 접수되면 ACCEPTED입니다. |
data.acceptedAt | String | 접수 시각(ISO 8601, +09:00). 같은 키로 다시 보내 첫 응답을 받으면 처음 접수한 시각입니다. |
data.msgType | String | SMS(단문) 또는 LMS(장문). 개발 키 응답에는 없습니다. |
traceId | String | 요청 추적 ID |
응답 헤더 X-Message-Key에도 messageId가 들어 있습니다. 같은 Idempotency-Key로 다시 보내 첫 응답을 돌려받을 때(Idempotent-Replayed: true)는 이 헤더가 없으니 본문의 messageId를 읽습니다.
단문과 장문은 치환한 본문의 길이로 정해지고, 무엇으로 접수됐는지는 data.msgType에 담깁니다. 길이 세는 법으로 미리 알 수도 있습니다.
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | SEND-0000 | 요청이 접수되었습니다. |
| 200 | SEND-0003 | 개발 키로 보낸 요청입니다. 모의로 접수했고 실제로 발송하지 않습니다. |
| 400 | SEND-4002 | 수신번호·발신번호 형식이 틀렸거나, 예약 시각이 지났거나 지금부터 30일을 넘었거나, 같은 Idempotency-Key로 내용(예약 시각 포함)이 다른 요청을 보냈습니다(문자 입력 오류). |
| 400 | SEND-4003 | 본문이 비었거나, 너무 길거나, 보낼 수 없는 글자가 있습니다(문자 입력 오류). |
| 400 | CMN-4001 | 필수 필드(수신번호·본문·발신번호)가 없거나, 요청 본문을 읽을 수 없습니다(JSON 형식 오류, 시간대가 없는 예약 시각 등). |
| 400 | SEND-4006 | 치환 변수를 채울 수 없습니다. |
| 400 | RATE-4001 | Idempotency-Key가 없거나 형식이 틀렸습니다. |
| 400 | RATE-4002 | 같은 Idempotency-Key로 다른 요청을 보냈습니다. 새 요청에는 새 키를 씁니다. |
| 401 / 403 | - | 토큰 오류 / 문자 채널 권한 없음·이용 정지(게이트웨이 오류) |
| 409 | SEND-4007, RATE-4091 | 같은 Idempotency-Key 요청이 처리 중. Retry-After 초만큼 기다린 뒤 같은 키로 다시 확인합니다. |
| 429 | RATE-4291, RATE-4292, RATE-4293 | 호출 한도·발송 쿼터·계약 초당 한도 초과 |
| 502 / 503 / 504 | SEND-5021 / CMN-5031 / SEND-5041 | 접수 여부를 알 수 없습니다. 같은 Idempotency-Key로 다시 보냅니다(다시 보내기). |
| 503 | SEND-5031 | 발송 채널을 잠시 쓸 수 없어 보내지 않았습니다. Retry-After 뒤 같은 키로 다시 보냅니다. |
응답 예시
{
"success": true,
"code": "SEND-0000",
"message": "요청이 접수되었습니다.",
"data": {
"messageId": "4f1c2a9e-0b7d-5e3a-9c41-2d8e6f0a1b3c",
"channel": "SMS",
"status": "ACCEPTED",
"acceptedAt": "2026-10-15T10:04:05.123+09:00",
"msgType": "SMS"
},
"traceId": "3f9c2a7b1d4e5f60"
}
본문이 너무 긴 경우의 예입니다.
{
"success": false,
"code": "SEND-4003",
"message": "본문이 2000바이트(한글 1000자)를 넘습니다.",
"data": null,
"traceId": "3f9c2a7b1d4e5f60"
}
문자
문자 대량 발송
한 번에 최대 500명에게 문자를 보냅니다. 같은 본문을 여러 명에게 보낼 때는 공통 블록(template)에 본문과 발신번호를 한 번만 싣고, 항목에는 수신자와 치환값만 담습니다.
참고
- 요청당 항목은 1~500건입니다. 더 많이 보내려면 500건씩 나눠 여러 번 호출합니다.
- 발송 쿼터를 항목 수만큼 차감합니다. 남은 쿼터가 항목 수보다 적으면 한 건도 보내지 않고 거절합니다(개발 키는 차감하지 않고 발송하지도 않습니다). 성공하지 않은 항목(거절·실패·확인 안 됨)의 쿼터는 결과가 나온 뒤 돌아옵니다.
- 발신번호는 이 API Key의 프로젝트에 등록된 번호여야 합니다. 공통 블록과 항목의 발신번호 중 하나라도 등록되지 않았으면 요청 전체를
400 SEND-4008로 거절합니다.
- 항목마다 접수·거절 결과가 따로 나옵니다. 일부만 거절돼도 HTTP는 200입니다.
- 응답의
batchKey로 이 요청에서 보낸 문자의 결과를 목록 조회할 수 있습니다.
- 단문과 장문은 항목마다 치환한 본문의 길이로 정해집니다(길이 세는 법). 접수된 항목의
msgType에 담깁니다.
- 예약 발송은 작성 방식에 따라
template.scheduledAt이나 items[].sms.scheduledAt에 시각을 넣습니다. 예약한 항목은 대량 예약 취소로 한꺼번에 취소할 수 있습니다.
작성 방식
| 방식 | 쓰는 경우 | 항목에 담는 것 |
| 템플릿 방식(권장) | 모든 수신자에게 같은 본문을 보냅니다. 본문과 발신번호는 template에 한 번만 씁니다. | recvNo, itemIdempotencyKey, clientRef, variables |
| 항목별 방식 | 수신자마다 본문이나 발신번호(또는 예약 시각)가 다릅니다. template을 보내지 않습니다. | 위 필드 + content, callbackNo, sms(예약 시각) |
두 방식은 섞을 수 없습니다. template을 보내면서 항목에 content나 sms를 넣으면 요청 전체를 거절합니다(400 CMN-4001).
예약 시각을 넣는 자리
- 템플릿 방식은
template.scheduledAt 하나로 모든 항목을 같은 시각에 예약합니다.
- 항목별 방식은 항목마다
items[].sms.scheduledAt을 넣습니다. template에 scheduledAt만 넣으면 무시되고, 예약 시각이 없는 항목은 바로 나갑니다. template.body·template.callbackNo가 없는 template은 쓰지 않은 것으로 봅니다.
요청
POST/api/v1/sms/batches
단건 발송과 같습니다. Idempotency-Key는 요청(배치)마다 새로 만듭니다.
요청 바디
| 필드 | 타입 | 필수 여부 | 설명 |
template | Object | 조건부 | 공통 블록. 템플릿 방식에서 필수입니다. |
template.body | String | 조건부 | 본문. #{변수} 자리는 항목의 variables로 채웁니다. 규칙은 단건 발송의 content와 같습니다. |
template.callbackNo | String | 조건부 | 발신번호. 숫자 8~20자리이고 하이픈은 있어도 됩니다. 이 API Key의 프로젝트에 등록된 발신번호만 쓸 수 있습니다. |
template.scheduledAt | String | 선택 | 예약 발송 시각. 템플릿 방식(template.body·callbackNo와 함께)에서만 쓰며, 모든 항목이 같은 시각에 나갑니다. 규칙은 단건 발송의 sms.scheduledAt과 같습니다. |
items | List | 필수 | 발송 항목 1~500건 |
items[].recvNo | String | 필수 | 수신 휴대폰 번호(단건 발송과 같은 규칙) |
items[].itemIdempotencyKey | String | 필수 | 항목 멱등 키. 1~64자
- 같은 배치 안에서 겹치면 그 항목을 거절합니다. 다른 요청과도 겹치지 않게 UUID나
{발송그룹ID}-{수신자ID}처럼 만듭니다.
- 같은 항목을 다시 보낼 때는 같은 값을 씁니다. 이미 접수된 항목은 새로 발송하지 않고 처음의
messageId를 돌려줍니다.
- 문자 단건 발송에 쓴
Idempotency-Key와도 겹치지 않게 만듭니다. 같은 값이면 이미 보낸 메시지로 보고 새로 보내지 않습니다.
|
items[].clientRef | String | 선택 | 내 쪽 수신자 식별자(64자 이하 — 넘으면 요청 전체가 400 CMN-4001). 해석하지 않고 보관합니다. 개인정보를 넣지 마십시오. |
items[].variables | Object | 조건부 | 이 수신자의 치환값. 본문에 #{변수}가 있으면 필수입니다(치환 변수). |
items[].content
items[].callbackNo | String | 조건부 | 항목별 방식에서만 씁니다. 의미는 단건 발송의 content·callbackNo와 같습니다. |
items[].sms.scheduledAt | String | 선택 | 항목별 방식에서 이 항목의 예약 발송 시각(단건 발송의 sms.scheduledAt과 같은 규칙). 항목마다 달라도 됩니다. 템플릿 방식에서는 template.scheduledAt을 씁니다. |
이 밖의 필드(발신 프로필 키·템플릿 코드·버튼 같은 알림톡 필드)는 문자에서 쓰지 않으며 보내도 무시됩니다.
요청 예시 — 템플릿 방식
curl -X POST 'https://{게이트웨이 주소}/api/v1/sms/batches' \
-H 'Authorization: Bearer {accessToken}' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 6f1e2d3c-4b5a-4c69-8d7e-1f2a3b4c5d6e' \
-d '{
"template": {
"callbackNo": "02-1234-5678",
"body": "[리노] #{name}님, 『#{surveyName}』 설문이 열렸습니다."
},
"items": [
{ "recvNo": "010-1234-5678", "itemIdempotencyKey": "notice7788-25780", "clientRef": "25780",
"variables": { "name": "홍길동", "surveyName": "도민 만족도 조사" } },
{ "recvNo": "010-9876-5432", "itemIdempotencyKey": "notice7788-25781", "clientRef": "25781",
"variables": { "name": "김영희", "surveyName": "도민 만족도 조사" } }
]
}'
모든 항목을 같은 시각에 예약하려면 template에 "scheduledAt": "2026-11-02T09:00:00+09:00"처럼 시각을 더합니다.
요청 예시 — 항목별 방식
수신자마다 본문과 예약 시각이 다른 요청 바디입니다. 두 번째 항목은 예약 시각이 없어 바로 나갑니다.
{
"items": [
{ "recvNo": "010-1234-5678", "itemIdempotencyKey": "remind7790-25780", "clientRef": "25780",
"content": "[리노] 홍길동님, 11월 2일 오전 10시 상담이 예약되어 있습니다.", "callbackNo": "02-1234-5678",
"sms": { "scheduledAt": "2026-11-01T18:00:00+09:00" } },
{ "recvNo": "010-9876-5432", "itemIdempotencyKey": "remind7790-25781", "clientRef": "25781",
"content": "[리노] 김영희님, 오늘 오후 3시 상담이 예약되어 있습니다.", "callbackNo": "02-1234-5678" }
]
}
응답
응답 바디
| 필드 | 타입 | 설명 |
success | Boolean | 요청 자체가 처리되면 true(일부 거절 포함) |
code | String | 모두 접수 SEND-0000, 1건 이상 거절 SEND-0002(개발 키는 SEND-0003) |
message | String | 예: 배치 요청이 접수되었습니다., 1건 접수, 1건 거절되었습니다. |
data.batchKey | String | 이 요청의 일괄키. 문의할 때 traceId와 함께 알려 주십시오. |
data.channel | String | SMS |
data.accepted / data.rejected | Integer | 접수·거절 항목 수 |
data.results[] | List | 항목별 결과. 요청 items와 같은 순서입니다. |
results[].index | Integer | 요청 items에서의 위치(0부터) |
results[].messageId | String | 접수된 항목의 메시지 키. 거절된 항목은 null |
results[].status | String | ACCEPTED | REJECTED |
results[].errorCode / errorMessage | String | 거절 이유(항목 거절 코드). 접수된 항목은 null |
results[].msgType | String | 접수된 항목의 유형 — SMS(단문) 또는 LMS(장문). 거절된 항목과 개발 키 응답에는 없습니다. |
traceId | String | 요청 추적 ID |
응답 헤더 X-Batch-Key에도 batchKey가 들어 있습니다. 같은 Idempotency-Key로 다시 보내 첫 응답을 돌려받을 때는 이 헤더가 없으니 본문의 batchKey를 읽습니다.
항목 결과를 읽는 법
messageId가 없는 항목은 접수되지 않은 것으로 처리하십시오. 이유는 errorCode·errorMessage에 있습니다.
- 입력 오류(
SEND-4002·SEND-4003)로 거절된 항목은 고쳐서, 그 밖의 거절은 그대로, 새 Idempotency-Key로 다시 보냅니다. 다시 보내는 항목의 itemIdempotencyKey는 바꾸지 않습니다.
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | SEND-0000 | 모든 항목이 접수되었습니다. |
| 200 | SEND-0003 | 개발 키로 보낸 요청입니다. 모든 항목을 모의로 접수했고 실제로 발송하지 않습니다. |
| 200 | SEND-0002 | 1건 이상 거절되었습니다. 항목별 결과를 확인합니다. |
| 400 | CMN-4001 | 요청 전체 거절. 항목이 없거나 500건 초과, 필드 검증 오류(data.fieldErrors[]), 공통 블록에 body·callbackNo 중 빠진 것이 있음, 두 방식을 섞음(템플릿 방식인데 항목에 content·sms가 있음), 항목별 방식인데 항목에 본문·발신번호가 없음(message에 items[n]), 요청 본문을 읽을 수 없음(JSON 형식 오류, 시간대가 없는 예약 시각 등 — 어느 항목인지는 알려 주지 않습니다) |
| 400 | SEND-4006 | 치환 변수를 채울 수 없습니다. message에 항목 번호(items[n])와 변수 이름이 나옵니다. 한 항목이라도 실패하면 요청 전체를 거절합니다. |
| 400 | SEND-4008 | 등록되지 않은 발신번호가 있습니다. 요청 전체를 거절합니다. |
| 400 | RATE-4001 | Idempotency-Key가 없거나 형식이 틀렸습니다. |
| 400 | RATE-4002 | 같은 Idempotency-Key로 다른 요청(다른 항목 구성)을 보냈습니다. 새 요청에는 새 키를 씁니다. |
| 401 / 403 | - | 토큰 오류 / 문자 채널 권한 없음·이용 정지(게이트웨이 오류) |
| 409 | RATE-4091 | 같은 Idempotency-Key 요청이 처리 중. Retry-After 초만큼 기다린 뒤 같은 키로 다시 확인합니다 |
| 429 | RATE-4291, RATE-4292, RATE-4293 | 호출 한도·발송 쿼터·계약 초당 한도 초과 |
| 502 / 503 / 504 | SEND-5021 / CMN-5031 / SEND-5041 | 항목마다 접수됐는지 알 수 없습니다(아래 참조). |
| 503 | SEND-5031 | 발송 채널을 잠시 쓸 수 없어 보내지 않았습니다. Retry-After 뒤 다시 보냅니다(아래와 같은 방법). |
502·503·504를 받았을 때
새 Idempotency-Key로, 항목의 itemIdempotencyKey는 그대로 다시 보내십시오. 이미 접수된 항목은 새로 발송되지 않고 처음의 messageId를 돌려받습니다. 아직 처리 중인 항목은 SEND-4007로 거절되니 잠시 뒤 같은 방법으로 다시 보냅니다.
응답 예시
{
"success": true,
"code": "SEND-0002",
"message": "1건 접수, 1건 거절되었습니다.",
"data": {
"batchKey": "b1f0a2c4111122223333444455556666",
"channel": "SMS",
"accepted": 1,
"rejected": 1,
"results": [
{ "index": 0, "messageId": "4f1c2a9e-0b7d-5e3a-9c41-2d8e6f0a1b3c", "status": "ACCEPTED",
"errorCode": null, "errorMessage": null, "msgType": "SMS" },
{ "index": 1, "messageId": null, "status": "REJECTED", "errorCode": "SEND-4002",
"errorMessage": "수신번호 형식이 올바르지 않습니다." }
]
},
"traceId": "3f9c2a7b1d4e5f60"
}
문자
문자 발송 결과 단건 조회
발송할 때 받은 메시지 키(messageId)로 문자 한 건의 발송 상태와 결과를 조회합니다.
참고
- 자기 API Key로 보낸 문자만 조회됩니다. 다른 키로 보낸 문자나 없는 메시지 키는 똑같이
404 CMN-4041로 응답합니다.
- 결과는 발송 시스템에서 30초마다 가져와 반영합니다. 30초보다 자주 조회해도 새 정보는 없습니다.
- 최종 상태는
SUCCESS·FAIL·UNCONFIRMED·CANCELLED 넷입니다. 그 밖의 상태는 아직 결과를 기다리는 중입니다(상태·결과 코드).
- 대량 발송의 한 항목도 그 항목의
messageId로 조회합니다. 한 번에 보낸 문자를 모아 보려면 목록 조회를 씁니다.
요청
GET/api/v1/sms/messages/{messageKey}
Authorization: Bearer {accessToken}가 필요합니다. 자세한 내용은 공통 요청 헤더를 참조해 주십시오.
요청 경로 파라미터
| 필드 | 타입 | 필수 여부 | 설명 |
messageKey | String | 필수 | 메시지 키. 발송 응답의 messageId 값입니다(예: 4f1c2a9e-0b7d-5e3a-9c41-2d8e6f0a1b3c). |
요청 예시
curl -X GET 'https://{게이트웨이 주소}/api/v1/sms/messages/4f1c2a9e-0b7d-5e3a-9c41-2d8e6f0a1b3c' \
-H 'Authorization: Bearer {accessToken}'
응답
형식 B로 옵니다.
응답 바디
| 필드 | 타입 | 설명 |
ok | Boolean | 성공하면 true |
data | Object | 문자 정보. 아래 Message 참조 |
traceId | String | 요청 추적 ID. 문의할 때 함께 알려 주십시오. |
값이 없는 필드는 null로 오지 않고 키 자체가 빠집니다. 예를 들어 결과를 기다리는 문자에는 resultCode·resultAt이 없습니다.
Message
조회 응답의 문자 한 건입니다. 목록 조회의 content에도 같은 모양으로 담깁니다.
| 필드 | 타입 | 설명 |
messageKey | String | 메시지 키(발송 응답의 messageId) |
status | String | 상태 코드(상태 코드). 항상 있습니다. |
resultCode | String | 결과 코드(결과 코드). 결과가 나오기 전에는 없습니다. |
resultAt | String | 결과 시각(ISO 8601, +09:00). 최종 상태일 때만 있습니다 — 단말에 전달된 문자는 전달된 시각, 취소한 문자는 취소한 시각입니다. |
recvNoMask | String | 가린 수신번호(예: 010-****-5678) |
clientRef | String | 대량 발송 때 넣은 참조값. 단건 발송 문자에는 없습니다. |
msgType | String | SMS(단문) 또는 LMS(장문) |
vendorResultCode | String | 발송사 원본 결과 코드 |
vendorResultCodeLabel | String | 출처를 붙인 원본 코드(예: NSC-0) |
resultMessage | String | 결과 설명 문구(결과 코드) |
본문·발신번호는 응답에 담지 않습니다.
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | - | 조회 성공 |
| 401 | CMN-4011, AUTH-4013 등 | 토큰이 없거나 만료·위조됨(게이트웨이 오류) |
| 404 | CMN-4041 | 대상을 찾을 수 없습니다(없는 키, 다른 API Key의 문자, 개발 키로 받은 mock- 키). |
| 429 | RATE-4290 | 조회 한도(API Key별 초당 60회, 목록 조회와 합산) 초과. Retry-After 초 뒤에 다시 호출합니다. |
| 503 | CMN-5031, EXT_SEND_DB_001 | 일시 장애. 잠시 뒤 다시 호출합니다. |
응답 예시
{
"ok": true,
"data": {
"messageKey": "4f1c2a9e-0b7d-5e3a-9c41-2d8e6f0a1b3c",
"status": "SUCCESS",
"resultCode": "SUCCESS",
"resultAt": "2026-10-26T14:07:42+09:00",
"recvNoMask": "010-****-5678",
"msgType": "SMS",
"vendorResultCode": "0",
"vendorResultCodeLabel": "NSC-0",
"resultMessage": "정상 발송되었습니다."
},
"traceId": "3f9c2a7b1d4e5f60"
}
오류일 때는 data 대신 error가 옵니다.
{
"ok": false,
"error": { "code": "CMN-4041", "message": "대상을 찾을 수 없습니다." },
"traceId": "3f9c2a7b1d4e5f60"
}
문자
문자 발송 결과 목록 조회
조건에 맞는 문자 목록을 조회합니다. 대량 발송 응답의 batchKey로 한 번에 보낸 문자의 결과를 모아 볼 수 있습니다.
참고
- 조회 기간은 접수 시각 기준입니다.
from·to를 생략하면 최근 31일을 조회하고, 한 번에 최대 92일까지 조회할 수 있습니다. 예약 문자도 보낸 시각이 아니라 접수한 시각으로 찾습니다.
batchKey를 지정해도 기간 조건은 함께 걸립니다. 31일보다 오래된 배치를 조회하려면 from·to를 지정해 주십시오.
- 자기 API Key로 보낸 문자만 결과에 포함됩니다.
요청
GET/api/v1/sms/messages
Authorization: Bearer {accessToken}가 필요합니다. 자세한 내용은 공통 요청 헤더를 참조해 주십시오.
요청 쿼리 파라미터
| 필드 | 타입 | 필수 여부 | 설명 |
from | String | 선택 | 조회 시작 일시(접수 시각 기준, 포함)
- 형식:
yyyy-MM-dd 또는 시간대가 붙은 ISO 8601 일시(예: 2026-10-26T09:00:00+09:00)
- 날짜만 쓰면 그날 00:00:00(KST)
- 기본값:
to의 31일 전
|
to | String | 선택 | 조회 종료 일시(포함)
- 형식은
from과 같습니다. 날짜만 쓰면 그날 23:59:59.999(KST)
- 기본값: 현재 시각
from보다 앞설 수 없고, 기간은 최대 92일
|
statusCd | String | 선택 | 상태 하나로 거릅니다. ACCEPTED | RESERVED | PENDING | SUCCESS | FAIL | UNCONFIRMED | CANCELLED (대소문자 구분, 상태 코드) |
resultCd | String | 선택 | 결과 코드 하나로 거릅니다. 결과 코드 10종 중 하나 |
batchKey | String | 선택 | 대량 발송 응답의 batchKey. 32자 이하, 영문·숫자·- |
page | Integer | 선택 | 조회할 쪽 번호. 0부터 시작합니다. 기본값 0 |
size | Integer | 선택 | 쪽당 건수. 1~100, 기본값 20 |
sort | String | 선택 | 정렬 기준. 필드,방향 형식이며 두 번까지 반복할 수 있습니다.
- 필드:
reqDtm(접수 시각) | resultAt(결과 시각) | status(상태)
- 방향:
asc | desc — 생략할 수 없습니다
- 기본값:
reqDtm,desc
|
요청 예시
curl -G 'https://{게이트웨이 주소}/api/v1/sms/messages' \
-H 'Authorization: Bearer {accessToken}' \
--data-urlencode 'batchKey=b1f0a2c4111122223333444455556666' \
--data-urlencode 'size=100'
응답
형식 B로 옵니다.
응답 바디
| 필드 | 타입 | 설명 |
ok | Boolean | 성공하면 true |
data.content | List | 문자 목록. Message 참조 |
data.page.number | Integer | 현재 쪽 번호(0부터) |
data.page.size | Integer | 쪽당 건수 |
data.page.totalPages | Integer | 전체 쪽 수 |
data.page.numberOfElements | Integer | 이번 쪽의 건수 |
data.page.first | Boolean | 첫 쪽 여부 |
data.page.last | Boolean | 마지막 쪽 여부 |
data.totalElements | Long | 조건에 맞는 전체 건수 |
traceId | String | 요청 추적 ID |
결과가 없으면 content는 빈 목록이고 totalPages는 0입니다. 마지막 쪽을 넘는 page를 요청해도 오류 없이 빈 목록을 돌려줍니다.
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | - | 조회 성공 |
| 400 | CMN-4000 | 파라미터가 올바르지 않습니다. error.message에 이유가 들어 있습니다(예: 조회 기간은 최대 92일까지 허용됩니다.). 여러 개가 틀려도 첫 번째 이유 하나만 알려 줍니다. |
| 401 | CMN-4011, AUTH-4013 등 | 토큰이 없거나 만료·위조됨 |
| 429 | RATE-4290 | 조회 한도(API Key별 초당 60회, 단건 조회와 합산) 초과. Retry-After 초 뒤에 다시 호출합니다. |
| 503 | CMN-5031, EXT_SEND_DB_001 | 일시 장애. 잠시 뒤 다시 호출합니다. |
응답 예시
{
"ok": true,
"data": {
"content": [
{
"messageKey": "9c3e5b1a-7d2f-5a4c-8b6e-1f0a2d4c6e8b",
"status": "ACCEPTED",
"recvNoMask": "010-****-9012",
"clientRef": "25782",
"msgType": "SMS"
},
{
"messageKey": "5d7a9c1e-3b4f-5e6a-9d8c-7b6a5f4e3d2c",
"status": "FAIL",
"resultCode": "FAIL_BLOCKED",
"resultAt": "2026-10-26T14:07:45+09:00",
"recvNoMask": "010-****-5432",
"clientRef": "25781",
"msgType": "SMS",
"vendorResultCode": "101",
"vendorResultCodeLabel": "NSC-101",
"resultMessage": "수신자가 문자를 받을 수 없는 상태입니다(수신 거부·스팸 차단). 재발송해도 같은 결과입니다."
},
{
"messageKey": "4f1c2a9e-0b7d-5e3a-9c41-2d8e6f0a1b3c",
"status": "SUCCESS",
"resultCode": "SUCCESS",
"resultAt": "2026-10-26T14:07:42+09:00",
"recvNoMask": "010-****-5678",
"clientRef": "25780",
"msgType": "SMS",
"vendorResultCode": "0",
"vendorResultCodeLabel": "NSC-0",
"resultMessage": "정상 발송되었습니다."
}
],
"page": {
"number": 0,
"size": 100,
"totalPages": 1,
"numberOfElements": 3,
"first": true,
"last": true
},
"totalElements": 3
},
"traceId": "3f9c2a7b1d4e5f60"
}
대량 발송 결과를 모으는 방법
- 대량 발송 응답의
batchKey와, 항목마다 넣은 clientRef를 저장해 둡니다. 결과는 clientRef로 내 쪽 기록과 맞춥니다(가린 번호는 겹칠 수 있습니다).
batchKey로 목록을 조회하고 last가 true가 될 때까지 쪽을 넘깁니다(size=100 권장).
status가 SUCCESS·FAIL·UNCONFIRMED·CANCELLED인 건은 확정입니다. 나머지는 다음 주기에 다시 조회합니다.
- 모든 건이 확정되면 그 배치의 조회를 끝냅니다.
주의
- 한 배치의 문자는 접수 시각이 같지만 쪽 사이의 순서는 고정됩니다. 다만 조회하는 동안 결과가 확정되면 정렬 값이 바뀌어 건이 다른 쪽으로 옮겨 갈 수 있습니다(상태순·결과 시각순으로 정렬한 경우). 확정되지 않은 건이 남아 있으면 처음 쪽부터 다시 조회하십시오.
statusCd는 값 하나만 받습니다. 결과를 기다리는 건만 보려면 ACCEPTED·RESERVED·PENDING을 각각 조회합니다.
- 발송 시스템 오류로 거절된 항목(
EXT_SEND_DB_001·EXT_SEND_DB_002)은 목록에 UNCONFIRMED로 나올 수 있습니다. 같은 itemIdempotencyKey로 다시 보내면 실제로 접수됐는지 확인한 뒤 한 번만 보냅니다(UNCONFIRMED 구분).
- 다시 보낸 항목은 처음 요청의
batchKey로 조회됩니다. 새 Idempotency-Key로 보낸 대량 발송은 batchKey가 달라도, 앞 요청에서 접수 단계까지 간 항목(입력 오류로 거절된 항목 밖)은 앞 요청의 batchKey에 남습니다.
- 운영자가 실패 건을 다시 보내면 같은
clientRef로 새 문자가 생기고, 그 문자는 원래 batchKey로는 조회되지 않습니다(운영자 재발송).
문자
문자 예약 취소
예약 발송한 문자 한 건을 보내기 전에 취소합니다. 발송할 때 받은 메시지 키(messageId)를 씁니다.
참고
- 예약 발송만 취소됩니다. 바로 보낸 문자는 접수하면 나가므로 취소할 수 없습니다(
409 SEND-4004).
- 예약 시각이 되어 발송 시스템이 가져간 뒤에는 취소할 수 없습니다(
409 SEND-4004).
- 이미 취소한 문자를 다시 취소해도 같은 답(
200, 처음 취소한 시각)이 옵니다. 응답을 못 받았으면 그대로 다시 호출하면 됩니다.
- 자기 API Key로 보낸 문자만 취소됩니다. 다른 키로 보낸 문자와 없는 메시지 키는 똑같이
404 CMN-4041입니다.
- 대량 발송의 한 항목도 그 항목의
messageId로 취소할 수 있습니다. 취소한 문자를 같은 키로 다시 보내도 다시 나가지 않습니다 — 새로 보내려면 새 키를 씁니다.
- 취소한 문자는 결과 조회에서
CANCELLED로 보입니다.
- 취소한 건의 발송 쿼터는 돌아옵니다(취소 뒤 몇 분 안, 호출 한도).
요청
DELETE/api/v1/sms/messages/{messageKey}
Authorization: Bearer {accessToken}가 필요합니다(공통 요청 헤더). Idempotency-Key는 쓰지 않습니다 — 취소는 몇 번 불러도 결과가 같습니다.
요청 경로 파라미터
| 필드 | 타입 | 필수 여부 | 설명 |
messageKey | String | 필수 | 메시지 키. 발송 응답의 messageId 값입니다(예: 4f1c2a9e-0b7d-5e3a-9c41-2d8e6f0a1b3c). |
요청 예시
curl -X DELETE 'https://{게이트웨이 주소}/api/v1/sms/messages/4f1c2a9e-0b7d-5e3a-9c41-2d8e6f0a1b3c' \
-H 'Authorization: Bearer {accessToken}'
응답
형식 B로 옵니다.
응답 바디
| 필드 | 타입 | 설명 |
ok | Boolean | 취소했으면(이미 취소한 문자 포함) true |
data.messageKey | String | 취소한 메시지 키 |
data.status | String | 늘 CANCELLED |
data.cancelledAt | String | 취소한 시각(ISO 8601, +09:00). 다시 취소하면 처음 취소한 시각입니다. |
data.traceId / traceId | String | 요청 추적 ID(둘이 같은 값) |
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | - | 취소했습니다(이미 취소한 문자 포함). |
| 401 | - | 토큰이 없거나 만료됐거나 올바르지 않습니다(게이트웨이 오류). |
| 404 | CMN-4041 | 대상을 찾을 수 없습니다(없는 키, 다른 API Key의 문자, 개발 키로 받은 mock- 키). |
| 409 | SEND-4004 | 취소할 수 없습니다 — 바로 보낸 문자, 또는 이미 나갔거나 나가는 중인 예약(예약 취소 오류). |
| 409 | SEND-4007 | 발송 접수를 아직 마치지 못했습니다. Retry-After 초 뒤 다시 취소합니다. |
| 503 | CMN-5031, EXT_SEND_DB_001, EXT_SEND_DB_002 | 일시 장애. 잠시 뒤 다시 호출합니다 — 취소는 다시 불러도 안전합니다. |
응답 예시
{
"ok": true,
"data": {
"messageKey": "4f1c2a9e-0b7d-5e3a-9c41-2d8e6f0a1b3c",
"status": "CANCELLED",
"cancelledAt": "2026-10-19T14:20:33.123+09:00",
"traceId": "3f9c2a7b1d4e5f60"
},
"traceId": "3f9c2a7b1d4e5f60"
}
바로 보낸 문자를 취소하려 한 경우의 예입니다.
{
"ok": false,
"error": { "code": "SEND-4004", "message": "바로 보낸 문자는 취소할 수 없습니다. 예약 발송만 취소합니다." },
"traceId": "3f9c2a7b1d4e5f60"
}
문자
문자 대량 예약 취소
대량 발송 응답의 batchKey로 그 요청에서 아직 나가지 않은 예약 항목을 한꺼번에 취소합니다.
참고
- 예약 항목 가운데 발송 시스템이 아직 가져가지 않은 것만 취소됩니다. 바로 보낸 항목, 이미 나갔거나 나가는 중인 항목, 접수를 아직 마치지 못한 항목은
skippedCount로 세고 그대로 둡니다. 일부만 취소돼도 200입니다.
- 앞서 취소한 항목은 어느 수에도 들지 않습니다 — 다시 부르면
cancelledCount가 0입니다.
- 어느 항목이 취소됐는지는 결과 목록 조회(
batchKey)에서 CANCELLED로 확인합니다.
- 응답을 못 받아 새
Idempotency-Key로 다시 보낸 대량 발송은 batchKey가 다릅니다. 앞 요청에서 접수된 항목은 앞 요청의 batchKey나 항목의 messageId(예약 취소)로 취소합니다.
- 자기 API Key로 보낸 대량 발송만 취소됩니다(다른 키·없는 키는
404 CMN-4041). 취소한 건의 쿼터는 돌아옵니다.
요청
DELETE/api/v1/sms/messages/batches/{batchKey}
예약 취소와 같습니다.
요청 경로 파라미터
| 필드 | 타입 | 필수 여부 | 설명 |
batchKey | String | 필수 | 대량 발송 응답의 data.batchKey(응답 헤더 X-Batch-Key와 같다) |
요청 예시
curl -X DELETE 'https://{게이트웨이 주소}/api/v1/sms/messages/batches/b1f0a2c4111122223333444455556666' \
-H 'Authorization: Bearer {accessToken}'
응답
형식 B로 옵니다.
응답 바디
| 필드 | 타입 | 설명 |
ok | Boolean | 처리했으면(일부만 취소한 경우 포함) true |
data.batchKey | String | 취소한 대량 발송의 batchKey |
data.cancelledCount | Integer | 이번에 취소한 항목 수 |
data.skippedCount | Integer | 취소하지 못한 항목 수 — 바로 보낸 항목, 이미 나갔거나 나가는 중인 항목, 접수를 아직 마치지 못한 항목 |
data.status | String | 늘 CANCELLED |
data.cancelledAt | String | 처리한 시각(ISO 8601, +09:00) |
data.traceId / traceId | String | 요청 추적 ID(둘이 같은 값) |
응답 상태 코드
| HTTP | 코드 | 설명 |
| 200 | - | 처리했습니다. 수는 cancelledCount·skippedCount에 있습니다. |
| 401 | - | 토큰이 없거나 만료됐거나 올바르지 않습니다(게이트웨이 오류). |
| 404 | CMN-4041 | 대상을 찾을 수 없습니다(없는 batchKey, 다른 API Key의 대량 발송, 입력 오류로 모든 항목이 거절된 대량 발송, 개발 키로 받은 mock- 키). |
| 503 | CMN-5031, EXT_SEND_DB_001, EXT_SEND_DB_002 | 일시 장애. 잠시 뒤 다시 호출합니다 — 다시 불러도 안전합니다. |
응답 예시
{
"ok": true,
"data": {
"batchKey": "b1f0a2c4111122223333444455556666",
"cancelledCount": 8,
"skippedCount": 2,
"status": "CANCELLED",
"cancelledAt": "2026-10-19T14:25:10.456+09:00",
"traceId": "3f9c2a7b1d4e5f60"
},
"traceId": "3f9c2a7b1d4e5f60"
}
문자
문자 상태·결과 코드
조회 응답의 status는 문자가 어디까지 왔는지, resultCode는 최종 결과가 무엇인지 알려 줍니다. 값은 알림톡과 다릅니다 — 알림톡의 SENDING·SENT, FAIL_TEMPLATE 같은 코드는 문자에 없습니다.
상태 코드 (status)
| 값 | 의미 | 최종 여부 | 할 일 |
ACCEPTED | 바로 보낸 문자가 접수되어 결과를 기다리는 중 | 아니오 | 다음 주기에 다시 조회 |
RESERVED | 예약 문자가 예약 시각을 기다리는 중 | 아니오 | 예약 시각 뒤에 결과가 반영됩니다. 그 전에는 예약 취소할 수 있습니다. |
PENDING | 발송사가 아직 결과를 확정하지 못함 | 아니오 | 다시 보내지 말고 계속 조회. 발송 시스템에 넣은 뒤(예약은 예약 시각부터) 25시간 안에 확정되지 않으면 UNCONFIRMED로 바뀝니다. |
SUCCESS | 발송 성공 | 예 | - |
FAIL | 발송 실패. 이유는 resultCode | 예 | 결과 코드별 안내를 따릅니다. |
UNCONFIRMED | 결과를 확인하지 못함 — 실패가 아닙니다. 문자가 실제로 나갔을 수 있습니다. | 예 | 아래 UNCONFIRMED 구분을 봅니다. |
CANCELLED | 발송 전에 취소됨 — 예약 취소 API, 운영자 취소, 고객사 이용 정지·해지에 따른 자동 취소(운영자 예약 취소) | 예 | - |
참고
결과가 오지 않아 ACCEPTED에 오래 머무는 문자가 있으면 messageKey와 발송 때 받은 traceId를 담당자에게 알려 주십시오.
UNCONFIRMED 구분
UNCONFIRMED에는 성격이 다른 두 경우가 함께 들어 있습니다. resultMessage로 나눠 처리해 주십시오.
| 경우 | resultMessage | 할 일 |
| 발송사가 정해진 시간 안에 결과를 확정하지 않았습니다. | 정해진 시간 안에 발송사로부터 결과를 확인하지 못했습니다. 문자가 실제로 도착했을 수 있으므로, 재발송 전에 수신 여부를 확인하세요. | 수신 여부를 확인하기 전에는 다시 보내지 않습니다. |
발송 요청이 발송 시스템 오류(단건의 502 SEND-5021, 대량 발송 항목의 EXT_SEND_DB_001·EXT_SEND_DB_002 등)로 끝나 접수됐는지 확인하지 못했습니다. | 발송사에 넣었는지 확인하지 못했습니다. 문자가 실제로 나갔을 수 있으므로, 재발송 전에 수신 여부를 확인하세요. | 같은 Idempotency-Key(대량은 같은 itemIdempotencyKey)로 다시 보냅니다. 실제로 접수됐는지 확인해 접수됐으면 처음 결과를 돌려주고, 아니면 한 번만 보냅니다 — 그 뒤로는 결과가 정상으로 반영됩니다. 새 키로 보내면 두 번 나갈 수 있습니다(다시 보내기). |
결과 코드 (resultCode)
resultMessage에는 아래 문구가 그대로 들어갑니다. 내 화면에 사유를 보여 줄 때 그대로 써도 됩니다.
| resultCode | status | resultMessage | 다시 보내도 되나 |
SUCCESS | SUCCESS | 정상 발송되었습니다. | 필요 없음 |
FAIL_RECIPIENT | FAIL | 수신번호가 올바르지 않거나 사용하지 않는 번호입니다. 번호를 확인한 뒤 다시 발송하세요. | 번호를 고친 뒤에만 |
UNDELIVERED | FAIL | 수신 단말이나 통신망 사유로 전달되지 않았습니다. 잠시 뒤 다시 발송할 수 있습니다. | 잠시 뒤 가능 |
FAIL_CONTENT_LENGTH | FAIL | 본문 길이 또는 형식이 규격을 벗어났습니다. 내용을 줄이거나 형식을 맞추세요. | 본문을 고친 뒤에 |
FAIL_POLICY | FAIL | 광고성 문자 발송 규정(야간 광고 제한·제한 업종)에 걸렸습니다. 발송 시각과 내용을 확인하세요. | 시각·내용을 고친 뒤에 |
FAIL_BLOCKED | FAIL | 수신자가 문자를 받을 수 없는 상태입니다(수신 거부·스팸 차단). 재발송해도 같은 결과입니다. | 아니오 |
FAIL_VENDOR_AUTH | FAIL | 발송사 연동 인증에 실패했습니다. 수신자 문제가 아니라 연동 설정 문제입니다 — 관리자에게 문의하세요. | 담당자가 고친 뒤에 |
FAIL_QUOTA | FAIL | 발송사 한도를 넘었습니다. 한도가 회복된 뒤 재발송하세요. | 한도가 회복된 뒤에 |
FAIL_VENDOR | FAIL | 발송사 시스템 오류로 실패했습니다. 잠시 후 재발송하세요. | 잠시 뒤 가능 |
UNCONFIRMED | UNCONFIRMED | 위 UNCONFIRMED 구분의 두 문구 중 하나 | 위 표를 따릅니다 |
resultCode가 없는 문자(ACCEPTED·RESERVED·PENDING)는 아직 결과를 기다리는 중입니다. PENDING에는 발송사가 보낸 원본 코드(vendorResultCode)만 있을 수 있습니다.
발송사 원본 코드
vendorResultCode는 발송사가 돌려준 원본 코드이고, vendorResultCodeLabel은 출처 NSC-를 붙인 표기입니다(예: NSC-0 성공, NSC-101 수신 거부). 문의할 때는 resultCode와 함께 vendorResultCodeLabel을 알려 주시면 원인을 빨리 찾을 수 있습니다.
문자
문자 오류 코드
문자에서만 나오는 오류입니다. 인증·호출 한도·발송 공통 코드는 공통 오류 코드에 있습니다.
문자 요청이 규격에 맞지 않으면 400으로 답하고, 이유를 message에 적습니다. 필드 형식은 SEND-4002, 본문은 SEND-4003, 빠진 필드와 읽을 수 없는 값은 CMN-4001입니다. 고친 뒤 새 Idempotency-Key로 보냅니다.
| code | message | 대처 |
CMN-4001 | 입력값이 올바르지 않습니다. (data.fieldErrors[]에 recvNo) | recvNo를 넣습니다. |
CMN-4001 | 문자 본문(content)이 필요합니다. | content를 넣습니다. |
CMN-4001 | 발신번호(callbackNo)가 필요합니다. | callbackNo를 넣습니다. |
CMN-4001 | 입력값이 올바르지 않습니다. (data가 null) | 요청 본문을 읽지 못했습니다. JSON 형식과 값의 타입을 확인합니다. 예약 시각에 시간대가 없을 때(2026-10-20T09:00:00)도 이 응답입니다 — +09:00처럼 시간대를 붙입니다. |
SEND-4002 | 수신번호 형식이 올바르지 않습니다. | 010·011·016~019로 시작하는 10~11자리 휴대폰 번호인지 확인합니다. |
SEND-4002 | 발신번호 형식이 올바르지 않습니다. | 숫자 8~20자리인지 확인합니다(하이픈은 괜찮습니다). |
SEND-4002 | 예약 시각은 지금부터 30일 안이어야 합니다. | 지금부터 30일 안의 시각으로 고쳐 보냅니다(정확히 30일 뒤까지 받습니다). |
SEND-4002 | 예약 시각은 지금보다 뒤여야 합니다. | 지금보다 뒤의 시각으로 고쳐 보냅니다. 응답을 못 받아 다시 보낸 요청이면 앞 요청은 접수되지 않은 것입니다 — 새 시각·새 키로 보냅니다. |
SEND-4002 | 같은 Idempotency-Key 로 내용이 다른 요청을 보냈습니다. 새 요청에는 새 키를 쓰세요. | 이 키로 먼저 보낸 요청과 내용이 다릅니다. 새 요청에는 새 키를 씁니다(먼저 보낸 요청은 그대로입니다). |
SEND-4003 | 본문이 비어 있습니다. | 치환한 뒤 본문이 비지 않았는지 확인합니다. |
SEND-4003 | 본문이 2000바이트(한글 1000자)를 넘습니다. | 본문을 줄입니다(길이 세는 법). |
SEND-4003 | 본문에 문자로 보낼 수 없는 글자(이모지 등)가 있습니다. | 이모지·특수 기호를 빼거나 일반 글자로 바꿉니다(보낼 수 없는 글자). |
SEND-4003 | 본문에 쓸 수 없는 제어문자가 있습니다. | 줄바꿈(\n)·탭 밖의 제어문자를 뺍니다. |
그 밖의 응답
| 코드 | HTTP | 메시지 | 대처 |
SEND-4008 | 400 | 등록되지 않은 발신번호입니다. 등록된 발신번호로 보내거나 담당자에게 등록을 요청하세요. | 이 API Key의 프로젝트에 등록된 발신번호로 보냅니다. 발신번호는 API Key가 속한 프로젝트마다 등록되므로, 다른 API Key로 보낼 수 있던 번호가 이 API Key로는 막힐 수 있습니다 — 그때는 담당자에게 이 API Key에서도 쓸 수 있게 요청합니다. 새로 등록했으면 새 Idempotency-Key로 보냅니다 — 같은 키는 이 응답을 그대로 돌려줍니다. |
SEND-4007 | 409 | 같은 요청이 처리 중입니다. 잠시 후 다시 확인하세요. | Retry-After(보통 2초) 뒤 같은 키로 다시 보내면 처음 접수 결과를 받습니다. 발송 시스템 응답이 늦었던 요청은 1분쯤 처리 중이 이어질 수 있습니다. |
SEND-5021 | 502 | 발송 채널 처리 중 오류가 발생했습니다. | 같은 키로 다시 보냅니다. 이미 접수됐으면 다시 나가지 않습니다. |
SEND-5031 | 503 | 발송 채널을 잠시 쓸 수 없어 보내지 않았습니다. 잠시 후 같은 키로 다시 보내세요. | Retry-After 뒤 같은 키로 다시 보냅니다. |
SEND-5041 | 504 | 발송 채널 응답이 지연되었습니다. | 같은 키로 다시 보냅니다. |
대량 발송 항목 거절 코드
대량 발송 응답 results[].errorCode에 담기는 코드입니다. 해당 항목만 접수되지 않았습니다.
| errorCode | errorMessage(예) | 대처 |
SEND-4002 | 수신번호 형식이 올바르지 않습니다., itemIdempotencyKey 는 1~64자여야 합니다., 같은 배치 안에 같은 itemIdempotencyKey 가 있습니다., 예약 시각은 지금보다 뒤여야 합니다., 예약 시각은 지금부터 30일 안이어야 합니다. | 수신번호·발신번호·예약 시각, itemIdempotencyKey(1~64자, 배치 안에서 겹치지 않게)를 고쳐 보냅니다. |
SEND-4002 | 같은 itemIdempotencyKey 로 내용이 다른 항목을 보냈습니다. 새 항목에는 새 키를 쓰세요. | 이 항목키로 먼저 보낸 항목과 내용이 다릅니다(먼저 보낸 항목은 그대로입니다). 새 항목이면 새 itemIdempotencyKey를 씁니다. |
SEND-4003 | 본문이 2000바이트(한글 1000자)를 넘습니다. 등 | 입력 오류의 본문 항목과 같습니다. |
SEND-4007 | 같은 요청이 처리 중입니다. 잠시 후 다시 확인하세요. | 같은 itemIdempotencyKey의 앞 요청이 아직 처리 중입니다. 잠시 뒤 그 항목을 같은 itemIdempotencyKey로 다시 보냅니다. |
EXT_SEND_DB_001 | 발송 시스템 일시 장애 — 잠시 후 재시도 | 이 항목은 보내지 않았습니다. 잠시 뒤 같은 itemIdempotencyKey로 다시 보냅니다. |
EXT_SEND_DB_002 | 발송 시스템 응답 지연 — 같은 요청으로 다시 확인 | 접수됐는지 확인하는 중입니다. 1분쯤 뒤 같은 itemIdempotencyKey로 다시 보내면, 접수됐으면 처음의 messageId를 돌려받고 아니면 한 번만 보냅니다. |
SEND-5021 | 발송 채널 응답에 이 항목의 결과가 없습니다. … | 접수됐는지 알 수 없습니다. 같은 itemIdempotencyKey로 다시 보냅니다. |
CMN-5000 | 서버 내부 오류가 발생했습니다. | 같은 itemIdempotencyKey로 다시 보냅니다. 계속되면 문의해 주십시오. |
발송 결과 조회 (형식 B)
결과 단건 조회·결과 목록 조회의 오류입니다. 응답은 error.code·error.message에 옵니다.
| 코드 | HTTP | 메시지 | 대처 |
CMN-4000 | 400 | 파라미터마다 다른 이유(예: 조회 기간은 최대 92일까지 허용됩니다., statusCd 값이 허용 범위를 벗어났습니다: SENT) | error.message를 보고 파라미터를 고칩니다. 알림톡의 상태·결과 값은 받지 않습니다(문자 상태·결과 코드). |
CMN-4041 | 404 | 대상을 찾을 수 없습니다. | 메시지 키를 확인합니다. 다른 API Key로 보낸 문자는 조회되지 않습니다. |
RATE-4290 | 429 | 요청 한도를 초과했습니다. | Retry-After(1초) 뒤 다시 조회합니다. |
EXT_SEND_DB_001 | 503 | 발송 시스템 일시 장애 — 잠시 후 재시도 | 잠시 뒤 다시 조회합니다. |
CMN-5000 | 500 | 서버 내부 오류가 발생했습니다. | 잠시 뒤 다시 조회합니다. 계속되면 문의해 주십시오. |
예약 취소 (형식 B)
예약 취소·대량 예약 취소의 오류입니다. 응답은 error.code·error.message에 옵니다.
| 코드 | HTTP | 메시지 | 대처 |
CMN-4041 | 404 | 대상을 찾을 수 없습니다. | 메시지 키·batchKey를 확인합니다. 다른 API Key로 보낸 문자는 취소되지 않습니다. |
SEND-4004 | 409 | 바로 보낸 문자는 취소할 수 없습니다. 예약 발송만 취소합니다. | 예약하지 않은 문자는 접수하면 바로 나갑니다. |
SEND-4004 | 409 | 이미 처리되어 취소할 수 없습니다. | 예약 시각이 되어 이미 나갔거나 나가는 중입니다. |
SEND-4007 | 409 | 발송 접수를 아직 마치지 못했습니다. 잠시 후 다시 취소하세요. | Retry-After(보통 2초) 뒤 다시 취소합니다. 접수 응답을 503으로 받은 예약이면, 같은 키로 다시 보내 접수 여부를 먼저 확인합니다. |
EXT_SEND_DB_001 | 503 | 발송 시스템 일시 장애 — 잠시 후 재시도 | 잠시 뒤 다시 취소합니다. 취소는 다시 불러도 안전합니다. |
EXT_SEND_DB_002 | 503 | 발송 시스템 응답 지연 — 같은 요청으로 다시 확인 | 취소됐는지 알 수 없습니다. 다시 취소하면 확인해서 답합니다. |
CMN-5000 | 500 | 서버 내부 오류가 발생했습니다. | 다시 취소합니다. 계속되면 문의해 주십시오. |
문자
문자 주의 사항
문자를 연동할 때 알아 둘 것들입니다. 모든 채널에 같은 규칙은 멱등 키·다시 보내기와 요청·응답 규칙을 참조해 주십시오.
아직 안 되는 것
| 기능 | 지금 |
| 장문 제목 | 제목을 따로 받지 않습니다. 필요하면 본문 첫 줄에 씁니다. |
| 광고 문자 | 광고 표시와 수신거부 번호를 붙이는 기능이 아직 없습니다. |
| 그림 문자(MMS) | 받지 않습니다. |
결과 조회
- 발송 응답은 "접수"까지만 알려 줍니다. 전달 결과는 목록 조회나 단건 조회로 가져갑니다.
- 결과 반영 주기가 30초이므로 1분 정도 간격으로 조회하면 충분합니다.
SUCCESS·FAIL·UNCONFIRMED·CANCELLED가 될 때까지 조회합니다. PENDING은 확정이 아니니 다시 보내지 말고 기다립니다.
UNCONFIRMED는 실패가 아닙니다. 다시 보내기 전에 UNCONFIRMED 구분을 봅니다.
- 예약 문자는 예약 시각까지
RESERVED입니다. 목록 조회의 기간은 접수 시각 기준이고 기본 31일, 최대 92일입니다. 오래된 배치는 from·to를 지정합니다.
예약 발송과 취소
- 단건은
sms.scheduledAt, 대량은 template.scheduledAt(템플릿 방식) 또는 items[].sms.scheduledAt(항목별 방식)에 시각을 넣으면 그 시각에 보냅니다. 시간대가 붙은 ISO 8601(예: 2026-10-20T09:00:00+09:00)이고 지금보다 뒤, 지금부터 30일 안이어야 합니다(정확히 30일 뒤까지 받습니다). 초 아래는 버립니다. 시간대가 없으면 400 CMN-4001입니다.
- 항목별 방식에서
template에 scheduledAt만 넣으면 무시되고 바로 나갑니다(작성 방식).
- 보내기 전까지 예약 취소(메시지 키)나 대량 예약 취소(
batchKey)로 취소할 수 있습니다. 예약 시각이 되어 발송 시스템이 가져간 뒤에는 취소할 수 없고, 바로 보낸 문자도 취소할 수 없습니다(409 SEND-4004).
- 예약 시각을 바꾸려면 취소한 뒤 새 키로 다시 예약합니다. 같은 키로 예약 시각만 바꿔 보내면 다른 요청으로 보고 거절합니다 — 단건은
400(RATE-4002 또는 SEND-4002), 대량은 같은 Idempotency-Key면 요청 전체가 400 RATE-4002, 아니면 같은 itemIdempotencyKey의 항목이 SEND-4002로 거절됩니다.
- 응답을 못 받아 다시 보낼 때 예약 시각이 이미 지났다면 — 앞 요청이 접수됐으면 처음 접수 결과를 그대로 돌려받고, 접수되지 않았으면
400 SEND-4002(예약 시각은 지금보다 뒤여야 합니다)입니다. 그때는 새 시각과 새 키로 보냅니다.
- 운영자도 콘솔에서 예약을 취소할 수 있고, 고객사 이용이 정지·해지되면 남은 예약은 자동으로 취소됩니다(운영자 예약 취소).
- 예약도 접수할 때 발송 쿼터를 차감하며, 보내는 날이 아니라 접수한 날·달의 쿼터로 셉니다. 취소한 건의 쿼터는 돌아옵니다(취소 뒤 몇 분 안, 호출 한도).
길이 세는 법
본문은 #{변수}를 채운 뒤의 길이로 셉니다.
| 글자 | 바이트 |
| 한글·한자, 그 밖의 기호(·, ★, ①, 『』, ※ 등) | 2 |
| 영문·숫자·공백, 키보드 기호(!, ?, -, ~ 등) | 1 |
줄바꿈(\n)·탭 | 1 |
| 종류 | 본문 길이 | 한글만 쓰면 |
| 단문(SMS) | 80바이트 이하 | 40자 |
| 장문(LMS) | 81~2,000바이트 | 1,000자 |
| 거절 | 2,000바이트 초과 | - |
윈도우 줄바꿈(\r\n)은 \n으로 바꿔서 셉니다. 맥 등에서 자모가 나뉜 채로 온 한글은 완성형으로 합친 뒤 셉니다.
보낼 수 없는 글자
본문은 문자 발송 시스템이 쓰는 한글 문자셋(CP949, 확장 완성형)으로 보냅니다. 여기에 없는 글자가 하나라도 있으면 400 SEND-4003(본문에 문자로 보낼 수 없는 글자(이모지 등)가 있습니다)으로 거절합니다. 워드·웹에서 복사한 본문에 자주 섞이는 글자는 이렇습니다.
| 보낼 수 없는 글자 | 바꿔 쓸 글자 |
| 이모지(😀, ❤, ✔ 등) | 빼거나 ♥, ★ 같은 기호로 |
| 원화 기호 ₩ | 전각 ₩ 또는 "원" |
| 줄표 –, — | 하이픈 - |
| 글머리표 • | 가운뎃점 · |
| © 같은 기호, é 같은 악센트 문자 | 빼거나 일반 영문으로 |
| 줄 바꿈 없는 공백(NBSP, 보이지 않게 섞입니다) | 일반 공백 |
다시 보내기
- 응답을 못 받았거나 502·503·504를 받았으면 접수됐는지 알 수 없습니다. 같은
Idempotency-Key로 다시 보내십시오. 먼저 보낸 요청이 접수됐으면 다시 나가지 않고 처음의 messageId를 돌려받습니다.
- 아직 처리 중이면
409 SEND-4007과 Retry-After가 옵니다. 그 초만큼 기다린 뒤 같은 키로 다시 확인합니다.
- 새 키로 바꿔 보내면 두 번 나갈 수 있습니다.
- 400(입력 오류)을 받았으면 고친 뒤 새 키로 보냅니다.
- 대량 발송은 항목마다 판정합니다. 거절된 항목만 모아 새
Idempotency-Key로 보내되, 항목의 itemIdempotencyKey는 바꾸지 않습니다.
개발 키로 시험할 때
- 개발 키로 보낸 문자는 실제로 나가지 않고
SEND-0003으로 답합니다. messageId는 mock-으로 시작합니다.
- 개발 키에서도 필수값(수신번호·본문·발신번호), 치환 변수, 등록된 발신번호인지(
SEND-4008), 예약 시각의 형식은 운영과 같이 검사합니다.
- 번호 형식, 본문 길이·글자, 예약 시각이 지났는지·30일 안인지는 실제로 보낼 때 검사합니다. 이것까지 확인하려면 검증 키로 시험합니다.
- 개발 키로 받은
mock- 키는 예약 취소·결과 조회할 수 없습니다(404). 예약 취소와 결과 조회는 검증 키로 보내서 시험합니다.
이메일
이메일 API
이메일 발송 API는 준비 중입니다.
지금 호출하면
POST /api/v1/email와 POST /api/v1/email/batches는 501 SEND-5011(해당 채널 발송 기능은 준비 중입니다)로 응답합니다. 이 채널을 쓰도록 허용되지 않은 키는 그보다 먼저 403 RATE-4031을 받습니다. 개통 일정은 담당자에게 문의해 주십시오.
개통되면
- 이 묶음에 발송, 대량 발송, 결과 조회, 데이터 타입, 상태·결과 코드, 오류 코드, 주의 사항 문서가 추가됩니다.
- 토큰, 요청·응답 규칙, 호출 한도, 멱등 키·다시 보내기는 공통 문서를 그대로 따릅니다. 알림톡을 이미 연동했다면 같은 토큰으로 같은 방식으로 호출합니다.