채팅 API로 문자 보내기: /chat-room과 /chat 사용법
KAT Cloud 채팅 API로 문자를 보내는 방법입니다. POST /chat-room으로 고객 번호의 채팅방을 만들고 POST /chat으로 보내면 SMS·LMS가 자동으로 정해집니다. subject 필드, 응답 필드, 오류 처리까지 정리했습니다.

KAT Cloud 채팅 API로 문자를 보내려면 POST /chat-room으로 내 채널과 고객 전화번호를 묶은 채팅방을 만들고, 받은 채팅방 ID로 POST /chat을 호출하면 됩니다. 메시지 타입은 KAT Cloud가 자동으로 정합니다. API 문서의 기준에 따르면 90바이트 이하 본문은 SMS, 90바이트를 넘거나 제목(subject)이 있으면 LMS, 이미지가 있으면 MMS로 나갑니다.
채팅 API의 구조: 채널, 채팅방, 채팅
KAT Cloud(캣클라우드)의 문자는 "누구에게 보내는가"보다 "어느 대화에 쓰는가"로 다룹니다. 그래서 수신 번호만 넣는 단건 발송 대신, 채널과 고객 번호의 조합인 채팅방을 먼저 정하고 그 안에 메시지를 씁니다.
| 리소스 | 의미 | 이 글에서 쓰는 값 |
|---|---|---|
| 그룹 | 서비스 운영 단위. 생성 시 전화번호가 자동 발급됨 | GET /group의 id |
| 채널 | 그룹에 연결된 전화번호(070 기본) | channelId, phoneNumber |
| 채팅방 | 채널과 고객 전화번호 조합의 대화 스레드 | POST /chat-room의 결과 |
| 채팅 | 채팅방 안의 개별 메시지 | POST /chat 응답의 chatId |
같은 채널과 같은 고객 번호는 항상 같은 채팅방을 씁니다. 그래서 고객이 답장하면 같은 채팅방에 이력이 이어지고, 내가 보낸 메시지와 고객 메시지를 한 스레드로 조회할 수 있습니다. 채팅 API는 비즈니스 요금제 그룹 또는 별도 허용된 그룹에서 쓸 수 있으며, 조건이 맞지 않으면 403 AU017이 반환됩니다. 범위 규칙은 API 문서의 사용 범위에 있습니다.

1단계: 채널 ID 확인하기
채팅방을 만들려면 발신할 채널의 channelId가 필요합니다. GET /group은 API 키로 접근할 수 있는 그룹 목록을 돌려주고, 각 그룹에 phoneNumber와 channelId가 들어 있습니다.
export KAT_API_KEY="콘솔에서 발급받은 키"
BASE="https://studio-gateway.zerocall.kr"
curl -s "$BASE/group" -H "x-api-key: $KAT_API_KEY"{
"code": "0",
"message": "Success",
"result": [
{
"id": 1,
"title": "매장A",
"phoneNumber": "07012345678",
"channelId": 100,
"status": "ACTIVE",
"recordingEnabled": true,
"createdAt": "2026-01-15T09:00:00.000Z"
}
]
}channelId는 자주 바뀌는 값이 아니므로 설정 파일이나 DB에 저장해 두고 씁니다. 그룹이 여러 개라면 매장이나 서비스별로 어느 채널에서 보낼지 미리 정해 둡니다.
2단계: 채팅방 만들기
POST /chat-room에 channelId와 고객 phoneNumber를 보냅니다. 전화번호는 숫자만, 0으로 시작하는 형식이어야 합니다. 응답 result에는 채팅방 ID 문자열이 들어 있습니다.
curl -s -X POST "$BASE/chat-room" \
-H "x-api-key: $KAT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"channelId": 100, "phoneNumber": "01098765432"}'
# {"code":"0","message":"Success","result":"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"}이 API는 같은 채널과 전화번호 조합의 채팅방이 이미 있으면 새로 만들지 않고 기존 채팅방 ID를 돌려줍니다. 따라서 보낼 때마다 호출해도 채팅방이 늘어나지 않습니다. 호출 수를 줄이고 싶다면 고객 번호와 채팅방 ID를 묶어 캐시해 둡니다.
3단계: 메시지 보내기
POST /chat의 필수 필드는 chatRoomId와 content 두 개입니다. 선택 필드로 subject(LMS 제목), imageList(MMS 이미지), externalMessageId(중복 방지용 외부 메시지 ID)가 있습니다.
curl -s -X POST "$BASE/chat" \
-H "x-api-key: $KAT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"chatRoomId": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", "content": "예약 확인되었습니다.", "externalMessageId": "ext-msg-001"}'응답에서 주로 보는 필드는 다음과 같습니다.
| 필드 | 설명 |
|---|---|
chatId | 메시지 ID. 발송 결과를 맞춰 볼 때 쓰는 키 |
sendPhoneNumber, recvPhoneNumber | 발신 번호(내 채널), 수신 번호(고객) |
sendStatus | 발송 상태. 처음에는 PENDING, 이후 SENT 또는 FAILED |
chatType | 자동으로 정해진 메시지 타입(SMS, LMS, MMS) |
resultStatus, resultMessage | 결과 코드와 메시지. 발송 완료 전에는 null |
Node.js 18 이상에서는 내장 fetch로 두 호출을 묶은 함수를 만들 수 있습니다.
// send-text.js — Node.js 18+
const BASE_URL = 'https://studio-gateway.zerocall.kr';
const API_KEY = process.env.KAT_API_KEY;
async function kat(method, path, body) {
const res = await fetch(`${BASE_URL}${path}`, {
method,
headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
body: body ? JSON.stringify(body) : undefined,
});
const json = await res.json().catch(() => ({}));
if (!res.ok || json.code !== '0') {
// 에러 형식: { status, code, message }
throw new Error(`${method} ${path} 실패: ${res.status} ${json.code} ${json.message}`);
}
return json.result;
}
const toDigits = (phone) => String(phone).replace(/\D/g, ''); // 010-9876-5432 → 01098765432
async function sendText({ channelId, to, content, subject, externalMessageId }) {
const chatRoomId = await kat('POST', '/chat-room', { channelId, phoneNumber: toDigits(to) });
return kat('POST', '/chat', { chatRoomId, content, subject, externalMessageId });
}
(async () => {
const [group] = await kat('GET', '/group');
const sms = await sendText({
channelId: group.channelId,
to: '010-9876-5432',
content: '예약 확인되었습니다.',
});
console.log(sms.chatId, sms.chatType, sms.sendStatus); // ... SMS PENDING
const lms = await sendText({
channelId: group.channelId,
to: '010-9876-5432',
subject: '예약 안내',
content: '내일 오후 3시 예약이 확정되었습니다. 변경이 필요하면 이 번호로 답장해 주세요.',
externalMessageId: 'booking-20261030-0001',
});
console.log(lms.chatId, lms.chatType); // ... LMS
})();JSON.stringify는 값이 undefined인 필드를 빼고 직렬화하므로, subject를 넘기지 않으면 요청에도 들어가지 않습니다. Python이라면 같은 흐름을 이렇게 씁니다.
import os
import re
import requests
BASE_URL = "https://studio-gateway.zerocall.kr"
HEADERS = {"x-api-key": os.environ["KAT_API_KEY"], "Content-Type": "application/json"}
def kat(method: str, path: str, body: dict | None = None):
r = requests.request(method, f"{BASE_URL}{path}", headers=HEADERS, json=body, timeout=10)
data = r.json()
if r.status_code >= 400 or data.get("code") != "0":
raise RuntimeError(f"{r.status_code} {data.get('code')} {data.get('message')}")
return data["result"]
def send_text(channel_id: int, to: str, content: str, subject: str | None = None) -> dict:
room_id = kat("POST", "/chat-room", {"channelId": channel_id, "phoneNumber": re.sub(r"\D", "", to)})
body = {"chatRoomId": room_id, "content": content}
if subject:
body["subject"] = subject
return kat("POST", "/chat", body)subject 필드와 SMS·LMS 자동 결정
채팅 API에는 메시지 타입을 고르는 필드가 없습니다. KAT Cloud가 본문 길이, 제목, 이미지 여부를 보고 정합니다. API 문서에 적힌 자동 결정 기준은 다음과 같습니다.
- SMS: 본문 90바이트 이하
- LMS: 본문 90바이트 초과, 또는 제목(
subject) 포함 - MMS:
imageList에 값이 1개 이상
여기서 놓치기 쉬운 점이 subject입니다. 본문이 짧아도 제목을 넣으면 LMS로 나갑니다. KAT Cloud 요금표 기준 초과분 1구간 단가는 SMS 10원, LMS 32원(부가세 별도)이므로, 짧은 알림에 습관처럼 제목을 붙이면 비용이 세 배 넘게 늘어납니다. 제목은 긴 공지처럼 받는 사람이 제목을 보고 내용을 가늠해야 할 때만 넣는 것이 좋습니다. 요금표는 요금 안내에서 확인할 수 있습니다.

90바이트는 일반적으로 영문·숫자 1바이트, 한글 2바이트로 계산하므로 한글만 쓰면 약 45자입니다. 길이 제한의 기준으로는 문자 발송 API(/message/send) 문서가 본문 최대 2,000자, 제목 최대 64자로 안내하고 있으니 채팅 API에서도 이 범위 안에서 쓰는 것이 안전합니다. 이미지를 붙이는 방법은 이미지 문자(MMS) 보내기에서, 문자 종류별 차이는 SMS·LMS·MMS·알림톡 차이에서 다룹니다.
발송 후 확인과 오류 처리
POST /chat이 성공했다는 것은 발송 요청이 접수됐다는 뜻이고, 고객에게 전달됐다는 뜻은 아닙니다. 응답의 sendStatus는 PENDING에서 시작해 SENT 또는 FAILED로 바뀌고, 최종 전달 여부는 resultStatus 결과 코드로 판단합니다. SMS는 4100, LMS·MMS는 6600이 성공 코드입니다. 상태는 GET /chat?chatRoomId=...의 대화 조회로 확인하거나 SEND_RESULT 웹훅으로 받습니다. 결과 코드를 다루는 방법은 문자 발송 결과 확인 글에 정리했습니다.
요청 단계에서 만나는 오류는 대부분 다음 셋입니다.
- 401: API 키가 없거나 잘못됨.
x-api-key헤더 값을 확인합니다. - 403
AU017: 비즈니스 요금제 그룹이나 별도 허용된 그룹이 아님. 키에 연결된 그룹을 확인합니다. - 429: Rate Limit(60초당 1,200회) 초과. 대량 발송은 큐에서 속도를 조절하고 잠시 뒤 재시도합니다. 재시도 설계는 API 에러 처리와 Rate Limit 대응 글을 참고합니다.
참고로 POST /message/send도 문자를 보낼 수 있지만 문서에 Deprecated로 표시되어 있고, 새 연동에는 채팅 API가 권장됩니다.
| 항목 | 채팅 API(/chat-room → /chat) | POST /message/send |
|---|---|---|
| 문서상 상태 | 새 연동에 권장 | Deprecated |
| 사용 범위 | 비즈니스 요금제 그룹 또는 허용 그룹 | 모든 사용자 |
| 대화 관리 | 채팅방 단위로 이력 누적 | 수신 번호 단위 단건 발송 |
| 상태 확인 | GET /chat, SEND_RESULT 웹훅 | GET /message/send-result/{chatId}, SEND_RESULT 웹훅 |
필드별 상세는 채팅방 API 문서와 채팅 API 문서에 있습니다.
자주 묻는 질문
채팅방은 메시지를 보낼 때마다 새로 만들어야 하나요?
KAT Cloud의 POST /chat-room은 같은 채널과 고객 전화번호 조합의 채팅방이 이미 있으면 기존 채팅방 ID를 그대로 돌려줍니다. 그래서 보낼 때마다 호출해도 채팅방이 늘어나지 않고, 한 고객과 주고받은 메시지는 같은 채팅방에 계속 쌓입니다.
subject를 넣으면 요금이 달라지나요?
KAT Cloud 채팅 API에서 subject를 넣으면 본문이 90바이트 이하라도 LMS로 발송됩니다. LMS는 SMS보다 단가가 높으므로(초과분 1구간 기준 SMS 10원, LMS 32원, 부가세 별도), 짧은 알림에는 제목을 넣지 않는 것이 비용 면에서 유리합니다.
개인 요금제에서도 채팅 API를 쓸 수 있나요?
KAT Cloud 채팅 API는 비즈니스 요금제 그룹 또는 별도 허용된 그룹에서만 쓸 수 있고, 조건이 맞지 않으면 403 AU017이 반환됩니다. 문자 발신 API인 POST /message/send와 GET /message/send-result/{chatId}는 모든 사용자가 쓸 수 있지만, 문서상 Deprecated이므로 새로 만드는 연동이라면 채팅 API를 기준으로 설계하는 것이 좋습니다.
- #문자 발송
- #채팅 API
- #SMS
- #LMS



