B2B API ZeroCall B2B API를 통해 사용자 관리, 그룹/채널 관리, 통화 기록 조회, 메시지 발송, 연락처/CRM 관리, 사업자 등록 등의 기능을 외부 시스템과 연동할 수 있습니다.
Base URL https://b2b-gateway.zerocall.kr/api/v1
Rate Limit 1,200 requests / 60초항목 값 인증 방식 API Key (x-api-key 헤더) Content-Type application/json문자 인코딩 UTF-8
공통 응답 형식 B2B API는 정상 응답이 기본적으로code,message,result형태로 감싸져 반환됩니다.
에러 응답은 interceptor 래핑 없이status,code,message형식으로 반환됩니다.
json
{
"code" : "0" ,
"message" : "Success" ,
"result" : {
"externalUserId" : "user_12345"
}
}
json
{
"status" : 404 ,
"code" : "UR001" ,
"message" : "존재하지 않는 사용자에요!"
} 아래 섹션의 Response 예시는 가독성을 위해 대부분 result 내부 payload만 표시합니다.
인증 모든 API 요청에는 x-api-key 헤더가 필요합니다. API Key는 B2B 파트너 계정에서 발급받을 수 있습니다.
API Key 형식 http
x-api-key: b2b_xxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx온보딩 URL 생성 사용자가 ZeroCall 서비스에 가입할 수 있는 온보딩 URL을 생성합니다. 생성된 URL을 통해 사용자가 가입을 완료하면 자동으로 B2B 클라이언트에 연결됩니다.
POST /auth/onboarding-url
Request Body 필드 타입 필수 설명 externalUserId string선택 고객사의 사용자 ID (가입 완료 시 매핑됨, 최대 255자) name string선택 토큰 이름 (관리용) expiresAt string선택 만료일시 ISO 8601 (기본: 30일 후) maxUses number선택 최대 사용 횟수 (미입력 시 무제한, 1~100,000) skipAppRedirect boolean선택 온보딩 완료 후 앱 설치 화면으로 이동하지 않음 (기본: false)
json
Request
{
"externalUserId" : "user_12345" ,
"name" : "마케팅 캠페인 A" ,
"expiresAt" : "2026-05-01T00:00:00.000Z" ,
"maxUses" : 100 ,
"skipAppRedirect" : true
}
Response
{
"onboardingUrl" : "https://app.zerocall.kr/auth/b2b-onboarding?token=..." ,
"expiresAt" : "2026-05-01T00:00:00.000Z"
} 온보딩 URL 목록 조회 생성한 온보딩 URL 목록을 조회합니다.
GET /auth/onboarding-url
json
Response
{
"urls" : [
{
"tokenId" : 1 ,
"name" : "마케팅 캠페인 A" ,
"onboardingUrl" : "https://app.zerocall.kr/auth/b2b-onboarding?token=..." ,
"expiresAt" : "2026-05-01T00:00:00.000Z" ,
"maxUses" : 100 ,
"currentUses" : 5 ,
"isActive" : true ,
"isValid" : true ,
"createdAt" : "2026-04-01T00:00:00.000Z"
}
]
} Response Fields 필드 타입 설명 tokenId number토큰 ID name string | null토큰 이름 onboardingUrl string온보딩 URL expiresAt string만료일시 (ISO 8601) maxUses number | null최대 사용 횟수 (null: 무제한) currentUses number현재 사용 횟수 isActive boolean활성 상태 isValid boolean유효 여부 (활성 + 미만료 + 사용 횟수 미초과) createdAt string생성일시 (ISO 8601)
온보딩 URL 비활성화 온보딩 URL을 비활성화합니다. 비활성화된 URL은 더 이상 사용할 수 없습니다.
DELETE /auth/onboarding-url/:tokenId
bash
curl -X DELETE "https://b2b-gateway.zerocall.kr/api/v1/auth/onboarding-url/1" \
-H "x-api-key: b2b_xxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"본인인증 URL 생성 사용자 본인인증을 위한 URL을 생성합니다.
POST /auth/identity-verification-url
Request Body 필드 타입 필수 설명 externalUserId string필수 고객사의 사용자 ID
json
Request
{
"externalUserId" : "user_12345"
}
Response
{
"verificationUrl" : "https://verify.zerocall.kr/..." ,
"expiresAt" : "2026-04-17T15:30:00.000Z"
} 예외 코드 설명 UR001존재하지 않는 사용자 UR006이미 본인인증 완료
사용자 관리 고객사의 사용자를 ZeroCall과 연동합니다.
결제 분산형 과금에서 사용자 크레딧과 결제수단 등록 URL을 관리합니다.
추가 결제 API Method Path 설명 GET/payment/user/credit-history사용자별 B2B 서비스 사용 크레딧 이력 조회 GET/payment/client/creditB2B client 크레딧 현황 조회 GET/payment/client/credit-historyB2B client 전체 크레딧 소모 이력 조회
그룹 관리 그룹은 전화번호와 연결된 서비스 단위입니다.
착신전환 설정 수정 PATCH /group/{groupId}/call-forward?externalUserId=user_12345
Request Body 필드 타입 필수 설명 isCallForwardEnabled boolean필수 착신전환 활성화 여부 callForwardNumber string조건부 착신전환 전화번호 (활성화 시 필수)
json
Request (착신전환 활성화)
{
"isCallForwardEnabled" : true ,
"callForwardNumber" : "01098765432"
}
Request (착신전환 비활성화)
{
"isCallForwardEnabled" : false
}
예외 코드 설명 GR001존재하지 않는 그룹 GR007착신전환 활성화 시 전화번호 필요
추가 그룹 API Method Path 설명 POST/group/{groupId}/member멤버 추가 DELETE/group/{groupId}/member/{groupMemberId}멤버 제거 POST/group/{groupId}/leave그룹 탈퇴 PATCH/group/{groupId}/member/{groupMemberId}/call-receive그룹원 전화 수신 여부 수정 GET/group/plans그룹 생성 가능 요금제 목록 조회 GET/group/{groupId}/ai-usage그룹 AI 사용량 조회 GET/group/{groupId}/integration-config외부 API 연동 설정 조회 PATCH/group/{groupId}/integration-config외부 API 연동 설정 수정 GET/group/{groupId}/certificate통신서비스 이용증명원 PDF 발급
전화번호 풀 그룹이나 채널 생성 시 발급할 수 있는 전화번호 샘플을 조회합니다.
발급 가능한 전화번호 조회 GET /phone-number-pool/available
json
Response
{
"isAvailable" : true ,
"phoneNumber" : "07012345678"
}
Response (발급 가능한 번호가 없는 경우)
{
"isAvailable" : false
} 채널 관리 채널은 그룹 내의 개별 전화번호입니다.
통화 기록 그룹의 통화 기록을 조회하고 관리합니다.
통화 기록 조회 GET /call-history?externalUserId=...&groupId=1
Query Parameters 파라미터 타입 필수 설명 externalUserId string필수 사용자 ID groupId number필수 그룹 ID cursor string선택 페이지네이션 커서 size number선택 조회 개수 (기본: 20, 최대: 100) search string선택 전화번호 또는 연락처 이름 검색 channelId number선택 특정 채널 필터
json
Response
{
"data" : [
{
"id" : "uuid-123" ,
"from" : "01012345678" ,
"to" : "07012345678" ,
"isInbound" : true ,
"status" : "ANSWERED" ,
"tag" : "AI_TO_DIRECT" ,
"duration" : 120 ,
"historySummary" : "예약 문의 후 담당자 연결" ,
"sttAIList" : [
{ "role" : "agent" , "text" : "안녕하세요." , "timestamp" : 1.2 }
] ,
"sttDirectList" : [
{ "role" : "customer" , "text" : "예약 변경하고 싶어요." , "timestamp" : 30.5 }
] ,
"channelId" : 1 ,
"createdAt" : "2026-04-17T14:00:00.000Z" ,
"isRecorded" : true
}
] ,
"nextCursor" : "eyJpZCI6MTIzfQ==" ,
"hasNext" : true
} cursor 포맷이 잘못되면 HTTP 400 BadRequestException이 반환됩니다.
녹음 파일 URL 조회 GET /call-history/{callHistoryId}/recording?externalUserId=user_12345
Query Parameters 파라미터 타입 필수 설명 externalUserId string필수 사용자 ID
json
Response
{
"callHistoryId" : "uuid-123" ,
"recordings" : [
{
"agentType" : "direct" ,
"recordingTarget" : "all" ,
"url" : "https://storage.example.com/recording.wav?..." ,
"expiresAt" : "2026-04-17T15:00:00.000Z" ,
"contentLength" : 1234567 ,
"contentType" : "audio/wav" ,
"supportsRange" : true
}
]
} 예외 코드 설명 CH001존재하지 않는 채널/통화기록 AU007권한 없음
녹음 파일 URL은 5분 후 만료 됩니다. 파일을 보관해야 하는 경우, URL 수신 후 즉시 다운로드하세요.
통화기록 이의 제기 POST /call-history/{callHistoryId}/claim?externalUserId=...
json
Request
{
"claimReason" : "실제 통화 결과와 태그가 달라 재검토 요청"
}
예외 코드 설명 CH001존재하지 않는 채널/통화기록 CH003이미 사용중이거나 존재하지 않는 전화번호 또는 이의제기 불가 통화기록 AU007권한 없음
미확인 부재중 전화 개수 조회 GET /call-history/unread-count?externalUserId=...&groupId=1
모든 미확인 통화기록 읽음 처리 PATCH /call-history/read-all?externalUserId=...&groupId=1
status 값 (CallStatus) 값 설명 ANSWERED응답됨 MISSED부재중 REJECTED거절됨 UNKNOWN알 수 없음
tag 값 (CallTag) 값 설명 AIAI 응대 DIRECT직원 응대 AI_TO_DIRECTAI에서 직원으로 전환 UNKNOWN알 수 없음
solveTag 값 (SolveTag) 값 설명 SOLVED해결됨 UNSOLVED미해결 ASSESSING검토 중
메시지 발송 SMS/LMS/MMS 메시지를 발송합니다.
채팅방 상세 조회 GET /chat-room/{chatRoomId}?externalUserId=...
응답 형식은 채팅방 목록 조회의 단일 객체와 동일합니다.
채팅방 생성 POST /chat-room?externalUserId=...
Request Body 필드 타입 필수 설명 channelId number필수 채널 ID phoneNumber string필수 상대 전화번호
json
Request
{
"channelId" : 1 ,
"phoneNumber" : "01098765432"
}
Response
"chatroom_abc123" 예외 코드 설명 CH001존재하지 않는 채널/통화기록
채팅방 나가기 DELETE /chat-room/{chatRoomId}?externalUserId=...
json
Response
"chatroom_abc123" 채팅 대화 조회 GET /chat?externalUserId=...&chatRoomId=chatroom_abc123
Query Parameters 파라미터 타입 필수 설명 externalUserId string필수 사용자 ID chatRoomId string필수 채팅방 ID cursorDate string선택 이 시점 이전 메시지 조회 (ISO) size number선택 조회 개수 (최대 200)
json
Response
[
{
"id" : "chat_001" ,
"channelId" : 1 ,
"chatRoomId" : "chatroom_abc123" ,
"groupId" : 1 ,
"sequence" : 12 ,
"content" : "예약은 3시로 도와드릴게요." ,
"senderType" : "OPERATOR" ,
"timestamp" : "2026-04-17T14:30:00.000Z"
}
] 010 메시지 발송 본인인증이 완료된 사용자의 휴대폰 번호를 기준으로 메시지를 발송하고, 결과를 조회합니다.
사업자 관리 B2B client 소속 사업자를 등록하고 그룹과 연결합니다. 사업자 정보는 별도 business 리소스로 저장되며, 현재 API key의 B2B client에 연결된 사업자만 조회됩니다.
사업자등록증 업로드 POST /business/upload/registration-certificate
지원 형식: PNG, JPG, JPEG, PDF, TIF, TIFF
json
Response
{
"key" : "business/cert_abc123.pdf"
} 예외 코드 설명 BS018파일 누락 BS019지원하지 않는 파일 형식 BS020파일 크기 초과
여러 그룹에 사업자 일괄 연결 POST /business/{businessId}/bulk-link
Request Body json
Request
{
"groupIds" : [ 1 , 2 , 3 ]
}
Response
{
"linkedCount" : 3 ,
"pendingCount" : 0 ,
"skippedCount" : 0 ,
"message" : "3개 그룹이 연결되었습니다"
} 예외 코드 설명 BS001사업자 정보 없음 BS005검증 완료된 사업자만 연결 가능
추가 사업자 API Method Path 설명 GET/business/{id}사업자 상세 조회 PATCH/business/{id}사업자 정보 수정 DELETE/business/{id}사업자 삭제 POST/business/{id}/transfer사업자 관리 양도 GET/business/{id}/my-owned-groups연결 가능한 그룹 목록 조회 DELETE/business/group/{groupId}그룹에서 사업자 연결 해제 GET/business/search/by-number/{businessNumber}사업자등록번호로 검색 GET/business/{id}/link-history연결 이력 조회 GET/business/{id}/usage-certificate이용 증명원 데이터 조회
매뉴얼 관리 AI 응대에 사용되는 매뉴얼과 매장 정보를 관리합니다.
매뉴얼 추가 POST /manual?externalUserId=...
Request Body 필드 타입 필수 설명 groupId number필수 그룹 ID title string필수 정책명 (최대 80자) instruction string필수 지시사항 (최대 1500자) isActive boolean선택 활성화 여부 (기본: true)
json
Request
{
"groupId" : 1 ,
"title" : "예약 안내" ,
"instruction" : "예약 문의 시 가능한 날짜와 시간을 안내해주세요." ,
"isActive" : true
}
매뉴얼 수정 PATCH /manual/{id}?externalUserId=...
json
Request
{
"title" : "예약 안내 (수정)" ,
"instruction" : "예약 문의 시 가능한 날짜와 시간, 인원수를 확인해주세요." ,
"isActive" : true
}
매뉴얼 삭제 DELETE /manual/{id}?externalUserId=...
매장 정보 등록/수정 PATCH /manual/store-info?externalUserId=...
Request Body 필드 타입 필수 설명 groupId number필수 그룹 ID address string선택 매장 주소 (최대 1500자) operationHours string선택 운영시간 (최대 1500자) basicInfo string선택 기본 정보 (최대 150자) bankInfo string선택 계좌 정보 (최대 500자) wifiInfo string선택 와이파이 안내 (최대 500자) parkingInfo string선택 주차 안내 (최대 500자)
json
Request
{
"groupId" : 1 ,
"address" : "서울시 강남구 테헤란로 123" ,
"operationHours" : "평일 09:00~18:00, 주말 휴무" ,
"basicInfo" : "예약제로 운영되는 강남점입니다." ,
"bankInfo" : "국민은행 123456-78-901234 제로콜" ,
"wifiInfo" : "SSID: ZeroCall_Guest / PW: zerocall1234" ,
"parkingInfo" : "건물 지하주차장 2시간 무료"
}
Response
{
"groupId" : 1 ,
"address" : "서울시 강남구 테헤란로 123" ,
"operationHours" : "평일 09:00~18:00, 주말 휴무" ,
"basicInfo" : "예약제로 운영되는 강남점입니다." ,
"bankInfo" : "국민은행 123456-78-901234 제로콜" ,
"wifiInfo" : "SSID: ZeroCall_Guest / PW: zerocall1234" ,
"parkingInfo" : "건물 지하주차장 2시간 무료"
} AI 비활성화 시간 설정 특정 시간대에 AI 응대를 비활성화합니다. 시간은 자정(00:00)부터의 분 단위로 입력합니다.
설정 수정 PATCH /pass-time-setting/{id}?externalUserId=...
json
Request
{
"isActive" : false
}
예외 코드 설명 PT001존재하지 않는 설정 AU007권한 없음
설정 상세 조회 GET /pass-time-setting/{id}?externalUserId=...
응답 형식은 설정 목록 조회의 단일 객체와 동일합니다.
예외 코드 설명 PT001존재하지 않는 설정 AU007권한 없음
설정 삭제 DELETE /pass-time-setting/{id}?externalUserId=...
예외 코드 설명 PT001존재하지 않는 설정 AU007권한 없음
웹훅 이벤트 발생 시 등록된 URL로 HTTP POST 요청을 전송합니다.
이벤트 설명 MESSAGE_RECEIVED고객으로부터 메시지 수신 SEND_RESULT메시지 발송 결과 수신 CALL_RECEIVED고객으로부터 수신 전화 유입
현재 B2B API의 웹훅 consumer 구현 기준으로 `MESSAGE_RECEIVED`, `SEND_RESULT`, `CALL_RECEIVED` 이벤트를 제공합니다.
공통 Payload 형식 모든 B2B 웹훅은 동일한 envelope 구조를 사용하며, 이벤트별 상세 필드는 `data` 내부에 담깁니다.
typescript
type WebhookEnvelope<T> = {
eventType: 'MESSAGE_RECEIVED' | 'SEND_RESULT' | 'CALL_RECEIVED';
timestamp: string; // ISO 8601
data: T;
};
// MESSAGE_RECEIVED
interface B2bMessageReceivedData {
chatId: string;
b2bClientId: number;
chatRoomId: string;
groupId: number;
channelId: number;
phoneNumber: string;
senderPhoneNumber: string;
content: string;
subject?: string;
imageList: string[];
externalMessageId?: string;
receivedAt: string; // ISO 8601
}
// SEND_RESULT
interface B2bSendResultData {
chatId?: string;
id?: string;
b2bClientId: number;
chatRoomId?: string;
groupId?: number;
channelId?: number;
content: string;
subject?: string;
imageList: string[];
externalMessageId?: string;
sendPhoneNumber?: string;
recvPhoneNumber?: string;
fromPhoneNumber?: string;
toPhoneNumber?: string;
sendStatus: 'SUCCESS' | 'FAILED';
chatType: 'SMS' | 'LMS' | 'MMS' | 'RCS' | 'KAO';
resultStatus: string;
resultMessage?: string;
createdAt: string; // ISO 8601
}
// CALL_RECEIVED
interface B2bCallReceivedData {
callHistoryId: string;
b2bClientId: number;
groupId: number;
groupName: string;
callerNumber: string;
callerName: string;
calledNumber: string;
handOffReason?: string;
receivedAt: string; // ISO 8601
}MESSAGE_RECEIVED 고객으로부터 메시지를 수신했을 때 전송됩니다.
json
{
"eventType" : "MESSAGE_RECEIVED" ,
"timestamp" : "2026-04-17T14:30:00.000Z" ,
"data" : {
"chatId" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ,
"b2bClientId" : 5 ,
"chatRoomId" : "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" ,
"groupId" : 10 ,
"channelId" : 2 ,
"phoneNumber" : "07012345678" ,
"senderPhoneNumber" : "01098765432" ,
"content" : "안녕하세요, 예약 문의드립니다." ,
"imageList" : [ ] ,
"receivedAt" : "2026-04-17T14:30:00.000Z"
}
} 필드 설명 필드 타입 설명 eventType MESSAGE_RECEIVED웹훅 이벤트 타입 timestamp string웹훅 전송 시각 (ISO 8601) data.chatId string메시지 ID (32자) data.b2bClientId numberB2B 클라이언트 ID data.chatRoomId string채팅방 ID (30자) data.groupId number그룹 ID data.channelId number채널 ID data.phoneNumber string수신 번호 (ZeroCall 번호) data.senderPhoneNumber string발신자 번호 (고객) data.content string메시지 내용 data.subject string제목. 없으면 필드가 생략될 수 있음 data.imageList string[]MMS 이미지 URL 배열. 수신 웹훅에서는 S3 key가 아니라 Presigned URL로 변환되어 전달됩니다. data.externalMessageId string외부 메시지 ID. 없으면 필드가 생략될 수 있음 data.receivedAt string메시지 수신 시각 (ISO 8601)
CALL_RECEIVED 고객으로부터 수신 전화가 유입되었을 때 전송됩니다. 앱 수신 푸시와 별개로, B2B 시스템이 인입 전화를 감지할 수 있도록 제공되는 이벤트입니다.
이 이벤트는 전화 연결 완료가 아니라 `인입 시점` 기준입니다. `handOffReason`은 인입 직후에는 없을 수 있으며, 값이 없으면 필드가 생략될 수 있습니다.
json
{
"eventType" : "CALL_RECEIVED" ,
"timestamp" : "2026-05-27T09:00:00.000Z" ,
"data" : {
"callHistoryId" : "call_history_xxxxxxxxxxxxxxxx" ,
"b2bClientId" : 5 ,
"groupId" : 10 ,
"groupName" : "테스트 그룹" ,
"callerNumber" : "01098765432" ,
"callerName" : "홍길동" ,
"calledNumber" : "07012345678" ,
"receivedAt" : "2026-05-27T09:00:00.000Z"
}
} 필드 설명 필드 타입 설명 eventType CALL_RECEIVED웹훅 이벤트 타입 timestamp string웹훅 전송 시각 (ISO 8601) data.callHistoryId string통화 이력 ID data.b2bClientId numberB2B 클라이언트 ID data.groupId number수신 그룹 ID data.groupName string수신 그룹 이름 data.callerNumber string발신자 번호 (고객) data.callerName string발신자 표시 이름. 연락처 매칭 결과가 없으면 발신 번호 또는 기본값으로 내려올 수 있음 data.calledNumber string수신 번호 (ZeroCall 번호) data.handOffReason string상담 연결 사유. 인입 직후에는 없을 수 있으며 없으면 필드가 생략될 수 있음 data.receivedAt string전화 인입 시각 (ISO 8601)
SEND_RESULT 메시지 사업자가 최종 발송 결과를 회신했을 때 전송됩니다.
채널 메시지(`/chat`) 결과는 chatId/chatRoomId/sendPhoneNumber/recvPhoneNumber 형태, 인증 휴대폰 메시지(`/verified-phone-chat`) 결과는 id/fromPhoneNumber/toPhoneNumber 형태로 전달됩니다.
현재 구현상 SEND_RESULT 웹훅은 Bizppurio 결과 수신 후에만 발행되므로, payload의sendStatus는 보통SUCCESS이며 실제 전달 성공/실패는 resultStatus로 판단해야 합니다.
json
{
"eventType" : "SEND_RESULT" ,
"timestamp" : "2026-04-17T14:35:00.000Z" ,
"data" : {
"chatId" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ,
"b2bClientId" : 5 ,
"chatRoomId" : "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" ,
"groupId" : 10 ,
"channelId" : 2 ,
"content" : "예약이 확정되었습니다." ,
"subject" : "예약 확정 안내" ,
"imageList" : [ ] ,
"externalMessageId" : "ext_msg_001" ,
"sendPhoneNumber" : "07012345678" ,
"recvPhoneNumber" : "01098765432" ,
"sendStatus" : "SUCCESS" ,
"chatType" : "LMS" ,
"resultStatus" : "4100" ,
"resultMessage" : "전달" ,
"createdAt" : "2026-04-17T14:35:00.000Z"
}
} 필드 설명 필드 타입 설명 eventType SEND_RESULT웹훅 이벤트 타입 timestamp string웹훅 전송 시각 (ISO 8601) data.chatId string메시지 ID (32자) data.id string인증 휴대폰 메시지 ID. `/verified-phone-chat` 결과에서 사용될 수 있음 data.b2bClientId numberB2B 클라이언트 ID data.chatRoomId string채팅방 ID (30자). `/verified-phone-chat` 결과에는 없음 data.groupId number그룹 ID. `/verified-phone-chat` 결과에는 없음 data.channelId number채널 ID. `/verified-phone-chat` 결과에는 없음 data.content string메시지 본문 data.subject string제목. 없으면 필드가 생략될 수 있음 data.imageList string[]첨부 목록 data.externalMessageId string요청 시 전달한 외부 메시지 ID. 없으면 생략될 수 있음 data.sendPhoneNumber string발신 번호. `/verified-phone-chat` 결과에서는 `fromPhoneNumber` data.recvPhoneNumber string수신 번호. `/verified-phone-chat` 결과에서는 `toPhoneNumber` data.sendStatus SUCCESS | FAILEDZeroCall이 메시지 사업자에게 요청을 넘긴 상태 data.chatType SMS | LMS | MMS | RCS | KAO메시지 타입 data.resultStatus string최종 발송 결과 코드 data.resultMessage string결과 코드의 한글 메시지. 없으면 필드가 생략될 수 있음 data.createdAt string메시지 생성 시각 (ISO 8601)
sendStatus 값 (ChatSendStatus) 값 설명 SUCCESSZeroCall이 메시지 사업자에게 발송 요청을 넘김 FAILED메시지 사업자 요청 단계에서 실패. 이 경우 SEND_RESULT 웹훅이 오지 않을 수 있음
chatType 값 (ChatType) 값 설명 SMS단문 메시지 LMS장문 메시지 MMS멀티미디어 메시지 RCSRCS 메시지 KAO카카오 알림톡
웹훅 처리 권장사항 Timeout: 10초 HTTP 2xx 응답 시 성공 처리 비동기 처리 후 즉시 응답 권장 Studio 연동 ZeroCall Studio API 토큰을 사용하여 사용자와 그룹을 B2B 클라이언트에 연결하거나 해제합니다. 연결 시 해당 그룹들은 B2B 클라이언트의 요금제로 전환됩니다.
그룹 연결 Studio API 토큰으로 사용자를 인증하고, 지정된 그룹들을 B2B 클라이언트에 연결합니다. 연결된 그룹은 B2B 클라이언트의 기본 요금제가 적용됩니다.
POST /api/v1/studio-link
Request Body 필드 타입 필수 설명 studioApiToken stringO Studio API 토큰 (원본) groupIds number[]O 연결할 그룹 ID 목록
bash
curl -X POST "https://b2b-gateway.zerocall.kr/api/v1/studio-link" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"studioApiToken": "zc_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"groupIds": [1, 2, 3]
}'Response json
Response
{
"userId" : 12345 ,
"linkedGroupIds" : [ 1 , 2 , 3 ] ,
"appliedPlan" : "B2B_BASIC"
} 필드 타입 설명 userId numberStudio API 토큰 소유자의 사용자 ID linkedGroupIds number[]실제로 연결된 그룹 ID 목록 appliedPlan string적용된 B2B 요금제
예외 코드 설명 SL001유효하지 않은 Studio API 토큰 SL002만료된 Studio API 토큰 SL003존재하지 않는 그룹 포함 SL004B2B 클라이언트 없음 SL005활성 요금제 없음 SL006그룹 소유자 아님
주의사항 • Studio API 토큰 소유자가 해당 그룹의 소유자여야 합니다. • 연결 시 그룹의 요금제가 B2B 클라이언트의 기본 요금제로 변경됩니다. 에러 처리 API 요청 실패 시 아래 형식으로 에러가 반환됩니다.
json
{
"status" : 401 ,
"code" : "BB003" ,
"message" : "유효하지 않은 API 키입니다."
} HTTP 상태 코드 코드 설명 400 잘못된 요청 (파라미터 오류) 401 인증 실패 (API Key 없음/무효/만료) 402 결제 필요 (크레딧 부족) 403 권한 없음 (리소스 접근 불가) 404 리소스 없음 409 충돌 (중복 등) 429 Rate Limit 초과 500 서버 오류
B2B API 에러 코드 (BB-series) 코드 HTTP 설명 BB001 404 지원되지 않는 고객사 BB002 401 API 키 필요 BB003 401 유효하지 않은 API 키 BB004 401 만료된 API 키 BB005 403 그룹 접근 권한 없음 BB006 403 사용자 접근 권한 없음 BB007 403 채널 접근 권한 없음 BB008 403 자체 본인인증 미허용 BB009 402 크레딧 부족 (서비스 정지) BB010 401 B2B 클라이언트 인증 실패 BB012 400 웹훅 중복 BB013 404 웹훅 미존재
권한 에러 코드 (AU-series) 코드 HTTP 설명 AU007 401 권한이 없습니다. (그룹 멤버/그룹장 권한 필요)
externalUserId, groupId 해석 실패는 AU 계열이 아닌 BB006, BB005로 반환됩니다.
사용자 에러 코드 (UR-series) 코드 HTTP 설명 UR001 404 존재하지 않는 사용자 UR002 406 이미 존재하는 사용자 UR005 403 본인인증 필요 UR006 409 이미 본인인증 완료 UR008 409 B2B 클라이언트에 이미 등록된 유저
그룹 에러 코드 (GR-series) 코드 HTTP 설명 GR001 404 존재하지 않는 그룹 GR003 400 그룹장은 탈퇴 불가 GR004 403 현재 요금제에서 AI 기능 불가 GR005 404 존재하지 않는 멤버
채널 에러 코드 (CH-series) 코드 HTTP 설명 CH001 404 존재하지 않는 채널 CH002 406 채널이 이미 존재 CH003 406 이미 사용중이거나 존재하지 않는 전화번호 CH004 409 전화번호가 다른 요청에 의해 처리 중 CH005 404 대표 채널 미설정
연락처 에러 코드 (CT-series) 코드 HTTP 설명 CT001 404 존재하지 않는 연락처 CT002 400 커스텀 속성 정의 오류 CT003 400 커스텀 데이터 오류 CT004 403 현재 요금제에서 CRM 불가 CT008 409 이미 존재하는 연락처
API별 주요 예외 (서비스 로직 기준) 아래 표는 실제 서비스 로직에서 throw 되는 예외 기준입니다. 공통으로 API Key/권한 검증에서 BB002~BB007, AU007 이 발생할 수 있습니다.
API 주요 예외 코드 비고 POST /auth/identity-verification-urlUR001`, `UR006대상 사용자 없음, 이미 본인인증 완료 /userUR001`, `UR003`, `UR007`, `UR008`, `BB008사용자 조회/삭제, 중복 등록, 자체 본인인증 비허용 /groupGR001`, `GR003`, `GR004`, `GR007`, `GR008~GR012`, `UR005그룹 멤버/요금제/착신전환/삭제 결제 제약 /channelCH001`, `CH003`, `CH004`, `CH006`, `UR001`, `GL004채널 없음/번호 점유/결제수단 미등록/타입 제약 /call-historyCH001`, `CH003`, `AU007통화기록 없음, 이의제기 불가, 권한 없음 /chat-roomCR001`, `CH001채팅방/채널 없음 /chatCR001채팅방 없음(조회/발송) /verified-phone-chatUR001`, `UR005사용자 없음, 본인인증 미완료 /contactCT001~CT008`, `GR001연락처/CRM 유효성, 커스텀 속성, CRM 플랜 제약 /businessBS001~BS022`, `GR001`, `UR001사업자 조회/등록/연결/파일업로드 제약 /manualMA001매뉴얼 없음 /pass-time-settingPT001설정 없음
커서 기반 API(/chat-room, /call-history, /contact)는 cursor 포맷이 잘못되면 코드형 에러 외에 HTTP 400 Bad RequestException이 반환됩니다.
메시지 발송 결과 코드 `resultStatus`는 결과 코드이며, `resultMessage`는 해당 코드의 한글 메시지입니다. 성공 판정은 아래 성공 코드 기준으로 처리하세요.
성공 판정 규칙 항목 값 successCodes 4100, 6600, 7000successMessage 전달
SMS 결과 코드 resultStatus resultMessage 4100 전달 4400 음영 지역 4401 단말기 전원 꺼짐 4402 단말기 메시지 저장 초과 4403 메시지 삭제 됨 4404 가입자 위치 정보 없음 4405 단말기 BUSY 4410 잘못된 번호 4411 NPDB 에러 4412 착신거절 4413 SMSC 형식오류 4414 비가입자, 결번, 서비스정지 4415 식별코드 오류 4420 기타에러 4421 타임아웃 4422 단말기 일시정지 4423 단말기 착신거부 4424 URL SMS 미지원폰 4425 단말기 호 처리 중 4426 재시도 한도 초과 4427 기타 단말기 문제 4428 시스템 에러 4430 스팸 4431 발송 제한 수신거부(스팸) 4432 회신번호 차단(개인) 4433 회신번호 차단(기업) 4434 회신번호 사전 등록제에 의한 미등록 차단 4435 KISA 신고 스팸 회신 번호 차단 4436 회신번호 사전 등록제 번호규칙 위반 4437 중복 발신 제한 4438 화이트리스트 문구 체크 4443 KISA 스팸 차단
LMS/MMS 결과 코드 resultStatus resultMessage 6600 전달 6601 타임 아웃 6602 핸드폰 호 처리 중 6603 음영 지역 6604 전원이 꺼져 있음 6605 메시지 저장 개수 초과 6606 잘못된 번호 6607 서비스 일시 정지 6608 기타 단말기 문제 6609 착신 거절 6610 기타 에러 6611 통신사의 SMC 형식 오류 6612 게이트웨이의 형식 오류 6613 서비스 불가 단말기 6614 핸드폰 호 불가 상태 6615 SMC 운영자에 의해 삭제 6616 통신사의 메시지 큐 초과 6617 통신사의 스팸 처리 6618 공정위의 스팸 처리 6619 게이트웨이의 스팸 처리 6620 발송 건수 초과 6621 메시지의 길이 초과 6622 잘못된 번호 형식 6623 잘못된 데이터 형식 6624 MMS 정보를 찾을 수 없음 6625 NPDB 에러 6626 080 수신거부(SPAM) 6627 발송 제한 수신거부(SPAM) 6628 회신 번호 차단(개인) 6629 회신 번호 차단(기업) 6630 서비스 불가 번호 6631 회신 번호 사전 등록제에 의한 미등록 차단 6632 KISA 신고 스팸 회신 번호 차단 6633 회신 번호 사전 등록제 번호 규칙 위반 6670 첨부파일 사이즈 초과(60K)
카카오(알림톡/친구톡) 결과 코드 resultStatus resultMessage 7000 전달 7101 카카오 형식 오류 7103 Sender key (발신프로필키) 유효하지 않음 7105 Sender key (발신프로필키) 존재하지 않음 7106 삭제된 Sender key (발신프로필키) 7107 차단 상태 Sender key (발신프로필키) 7108 차단 상태 카카오톡 채널 (카카오톡 채널 운영툴에서 확인) 7109 닫힌 상태 카카오톡 채널 (카카오톡 채널 운영툴에서 확인) 7110 삭제된 카카오톡 채널 (카카오톡 채널 운영툴에서 확인) 7111 삭제 대기 상태의 카카오톡 채널 (카카오톡 채널 운영툴에서 확인) 7112 유효하지 않은 사업자번호 7125 메시지 차단 상태의 카카오톡 채널 (카카오톡 채널 운영툴에서 확인) 7203 친구톡 전송 시 친구 대상 아님 7204 템플릿 불일치 7300 기타 에러 7305 성공 불확실(30일 이내 수신 가능) 7306 카카오 시스템 오류 7308 전화번호 오류 7311 메시지가 존재하지 않음 7314 메시지 길이 초과 7315 템플릿 없음 7318 메시지를 전송할 수 없음 7322 메시지 발송 불가 시간 7323 메시지 그룹 정보를 찾을 수 없음 7324 재전송 메시지 존재하지 않음 7325 변수 글자수 제한 초과 7326 상담/봇 전환 버튼 글자수 제한 초과 7327 버튼/바로 연결 내용과 템플릿 불일치 7328 메시지 강조 표기 타이틀과 템플릿 불일치 7329 메시지 강조 표기 타이틀 길이 제한 초과 (50 자) 7330 메시지 타입과 템플릿 강조유형이 일치하지 않음 7331 헤더가 템플릿과 일치하지 않음 7332 헤더 길이 제한 초과(16 자) 7333 아이템 하이라이트가 템플릿과 일치하지 않음 7334 아이템 하이라이트 타이틀 길이 제한 초과 (이미지 없는 경우 30 자, 이미지 있는 경우 21 자) 7335 아이템 하이라이트 디스크립션 길이 제한 초과 (이미지 없는 경우 19 자, 이미지 있는 경우 13 자) 7336 아이템 리스트가 템플릿과 일치하지 않음 7337 아이템 리스트의 아이템의 디스크립션 길이 제한 초과(23 자) 7338 아이템 요약정보가 템플릿과 일치하지 않음 7339 아이템 요약정보의 디스크립션 길이 제한 초과(14 자) 7340 아이템 요약정보의 디스크립션에 허용되지 않은 문자 포함 (통화기호/코드, 숫자, 콤마, 소수점, 공백을 제외한 문자 포함) 7341 와이드 아이템 리스트 갯수, 최소 최대 갯수 불일치 7342 대표링크가 템플릿과 일치하지 않음 7351 캐러셀 아이템 리스트 갯수, 최소 최대 갯수 불일치 7352 캐러셀 아이템 메시지 길이 OVER 7421 타임아웃 7521 중복발신제한
코드 예시 cURL bash
# 사용자 생성
curl -X POST "https://b2b-gateway.zerocall.kr/api/v1/user" \
-H "x-api-key: b2b_xxxx_your-secret-key" \
-H "Content-Type: application/json" \
-d '{
"externalUserId": "user_12345",
"phoneNumber": "01012345678"
}'
# 그룹 목록 조회
curl -X GET "https://b2b-gateway.zerocall.kr/api/v1/group?externalUserId=user_12345" \
-H "x-api-key: b2b_xxxx_your-secret-key"
# 메시지 발송
curl -X POST "https://b2b-gateway.zerocall.kr/api/v1/chat?externalUserId=user_12345" \
-H "x-api-key: b2b_xxxx_your-secret-key" \
-H "Content-Type: application/json" \
-d '{
"chatRoomId": "room_123",
"content": "안녕하세요. 예약이 확정되었습니다."
}'
# 통화 기록 조회
curl -X GET "https://b2b-gateway.zerocall.kr/api/v1/call-history?externalUserId=user_12345&groupId=1&size=20" \
-H "x-api-key: b2b_xxxx_your-secret-key"JavaScript javascript
const axios = require('axios');
const client = axios.create({
baseURL: 'https://b2b-gateway.zerocall.kr/api/v1',
headers: {
'x-api-key': 'b2b_xxxx_your-secret-key',
'Content-Type': 'application/json'
}
});
// 사용자 생성
async function createUser(externalUserId, phoneNumber) {
const response = await client.post('/user', {
externalUserId,
phoneNumber
});
return response.data;
}
// 그룹 목록 조회
async function getGroups(externalUserId) {
const response = await client.get('/group', {
params: { externalUserId }
});
return response.data;
}
// 메시지 발송
async function sendMessage(externalUserId, chatRoomId, content) {
const response = await client.post('/chat', {
chatRoomId,
content
}, {
params: { externalUserId }
});
return response.data;
}
// 통화 기록 조회
async function getCallHistories(externalUserId, groupId, cursor) {
const response = await client.get('/call-history', {
params: { externalUserId, groupId, cursor, size: 20 }
});
return response.data;
}Python python
import requests
BASE_URL = 'https://b2b-gateway.zerocall.kr/api/v1'
API_KEY = 'b2b_xxxx_your-secret-key'
headers = {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
}
# 사용자 생성
def create_user(external_user_id: str, phone_number: str) -> dict:
response = requests.post(
f'{BASE_URL}/user',
headers=headers,
json={
'externalUserId': external_user_id,
'phoneNumber': phone_number
}
)
return response.json()
# 그룹 목록 조회
def get_groups(external_user_id: str) -> list:
response = requests.get(
f'{BASE_URL}/group',
headers=headers,
params={'externalUserId': external_user_id}
)
return response.json()
# 메시지 발송
def send_message(external_user_id: str, chat_room_id: str, content: str) -> dict:
response = requests.post(
f'{BASE_URL}/chat',
headers=headers,
params={'externalUserId': external_user_id},
json={
'chatRoomId': chat_room_id,
'content': content
}
)
return response.json()
# 통화 기록 조회
def get_call_histories(external_user_id: str, group_id: int, cursor: str = None) -> dict:
params = {
'externalUserId': external_user_id,
'groupId': group_id,
'size': 20
}
if cursor:
params['cursor'] = cursor
response = requests.get(
f'{BASE_URL}/call-history',
headers=headers,
params=params
)
return response.json()웹훅 수신 (Node.js) javascript
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook', (req, res) => {
const { eventType, timestamp, data } = req.body;
switch (eventType) {
case 'MESSAGE_RECEIVED':
console.log('메시지 수신:', data.content);
console.log('발신자:', data.senderPhoneNumber);
handleIncomingMessage(data);
break;
case 'SEND_RESULT':
console.log('사업자 요청 상태:', data.sendStatus);
console.log('최종 결과 코드:', data.resultStatus);
if (!['4100', '6600', '7000'].includes(data.resultStatus)) {
console.error('최종 발송 실패:', data.resultMessage);
}
handleSendResult(data);
break;
case 'CALL_RECEIVED':
console.log('수신 전화 인입:', data.calledNumber);
console.log('발신자:', data.callerNumber, data.callerName);
handleIncomingCall(data);
break;
}
// 즉시 200 응답 (비동기 처리 권장)
res.status(200).send('OK');
});
app.listen(3000);최종 업데이트: 2026년 5월 27일
문의사항이 있으시면 flover@flover.ai 로 연락해주세요.