ZeroCall
v1.1콘솔
개발자 문서Studio API

Studio API

ZeroCall Studio API를 사용하여 문자 메시지 발송, 수신 관리, 통화 기록 조회, 그룹 관리 등의 기능을 외부 시스템과 연동할 수 있습니다.

Base URL

https://studio-gateway.zerocall.kr

Rate Limit

1,200 requests / 60초
항목
인증 방식API Key (x-api-key 헤더)
Content-Typeapplication/json
문자 인코딩UTF-8

공통 성공 응답

성공 응답은 기본적으로 code,message,result 구조를 사용합니다. 아래 API 예시에서는 가독성을 위해 result 값만 표시할 수 있습니다.

json
{
  "code": "0",
  "message": "Success",
  "result": {
    "example": "value"
  }
}

인증

모든 API 요청에는 x-api-key 헤더가 필요합니다. API Key는 ZeroCall 콘솔에서 발급받을 수 있습니다.

API Key 사용 예시

콘솔에서 발급받은 API Key 전체 문자열을 x-api-key 헤더에 그대로 넣어 사용하세요.

http
x-api-key: {API_KEY}

인증 확인

API Key가 유효한지 확인합니다.

GET/auth-check
json
// Response
{
  "apiKeyId": 123,
  "userId": 456,
  "name": "My API Key",
  "groupIdList": [1, 2, 3]
}

그룹 관리

그룹은 전화번호와 연결된 서비스 단위입니다. 그룹을 생성하면 전화번호가 자동 발급됩니다.

그룹 목록 조회

API Key로 접근 가능한 그룹 목록을 조회합니다.

GET/group
json
// Response
[
  {
    "id": 1,
    "title": "매장A",
    "phoneNumber": "07012345678",
    "channelId": 100,
    "status": "ACTIVE",
    "createdAt": "2024-01-15T09:00:00.000Z"
  }
]

그룹 생성

새 그룹을 생성하고 전화번호를 발급받습니다.

POST/group

Request Body

필드타입필수설명
phoneNumberstring선택특정 번호 지정 (미입력 시 랜덤 발급)
groupTitlestring선택그룹 이름 (미입력 시 자동 생성)
json
// Request
{
  "groupTitle": "신규 매장",
  "phoneNumber": "07098765432"
}

// Response
{
  "groupId": 123
}

그룹 삭제

그룹을 삭제하고 전화번호를 반납합니다.

DELETE/group/{groupId}
파라미터타입설명
groupIdnumber삭제할 그룹 ID

성공 시 204 No Content 응답

전화번호 풀

발급 가능한 전화번호를 확인합니다.

GET/phone-number-pool/available
json
// Response
{
  "isAvailable": true,
  "phoneNumber": "07012345678"
}
필드타입설명
isAvailableboolean발급 가능 여부
phoneNumberstring샘플 번호 (1개만 반환)

메시지 발송

SMS/LMS/MMS 메시지를 발송합니다. 메시지 길이, 제목, 이미지 첨부 여부에 따라 타입이 자동 결정됩니다.

MMS 이미지 업로드

MMS 발송 전에 이미지 파일을 업로드합니다. 반환된 key를 /message/send imageList에 넣어 사용합니다.

POST/message/media/upload

Request Body

필드타입필수설명
filesFile[]필수multipart/form-data 파일 목록. 최대 3개

응답의 result.urls에는 업로드에 성공한 파일의 key 목록이 들어갑니다. 업로드에 실패한 파일은 이 배열에 포함되지 않습니다.

bash
curl -X POST \
  "https://studio-gateway.zerocall.kr/message/media/upload" \
  -H "accept: */*" \
  -H "x-api-key: {API_KEY}" \
  -H "Content-Type: multipart/form-data" \
  -F "files=@sample1.png;type=image/png" \
  -F "files=@sample2.jpg;type=image/jpg"
json
// Response result
{
  "urls": [
    "chat-media/123456/1713776400000-abcd123.jpg",
    "chat-media/123456/1713776400001-efgh456.jpg"
  ]
}

이미지 파일 조건

  • • 한 번에 최대 3개까지 업로드할 수 있습니다.
  • • 이미지 파일은 업로드 후 JPG로 자동 변환되고, 가능하면 MMS 규격에 맞게 축소됩니다.
  • • 권장 조건은 최대 1280px, 300KB 이하입니다.
  • • 자동 축소 후에도 300KB 이하로 충분히 줄지 않는 이미지가 있을 수 있으니, 가능하면 권장 크기에 맞춰 업로드해 주세요.
  • • 일부 이미지는 자동 축소 후에도 MMS 규격을 만족하지 못해 전송이 실패할 수 있습니다.
  • • 반환된 key를 그대로 `/message/send`의 `imageList`에 넣으면 MMS로 발송됩니다.

메시지 전송

POST/message/send

Request Body

필드타입필수설명
toPhoneNumberstring필수수신자 전화번호 (숫자만)
contentstring필수메시지 내용 (최대 2,000자)
fromPhoneNumberstring선택발신 번호 (숫자만, 내 그룹 번호만 가능)
subjectstring선택LMS 제목 (최대 64자)
imageListstring[]선택이미지/동영상 목록 (S3 key 또는 URL). 값이 있으면 MMS로 발송

이 API의 result에는 32자 chatId 문자열이 들어갑니다. 이 값은 이후 SEND_RESULT 웹훅의 data.chatId와 매칭됩니다.

json
// Request
{
  "toPhoneNumber": "01098765432",
  "content": "안녕하세요. 예약이 확정되었습니다.",
  "fromPhoneNumber": "07012345678",
  "subject": "예약 확정 안내"
}

// Response result
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

메시지 타입 자동 결정

  • SMS - 90바이트 이하
  • LMS - 90바이트 초과 또는 제목 포함
  • MMS - imageList에 값이 1개 이상 존재

result에 들어 있는 chatId는 웹훅의 SEND_RESULT 이벤트에서 발송 결과를 식별하는 데 사용됩니다.

메시지 발송 결과 조회

/message/send에서 반환된 chatId로 최종 발송 결과를 조회합니다. 웹훅을 사용하지 않거나, 웹훅 수신 전 상태를 직접 확인해야 할 때 사용할 수 있습니다.

GET/message/send-result/{chatId}

Path Parameters

파라미터타입필수설명
chatIdstring필수/message/send 응답으로 받은 메시지 ID (32자)

이 API는 SEND_RESULT 웹훅과 동일한 최종 결과 정보를 조회용으로 제공합니다. 성공 여부는 resultStatus로 판단하세요.

bash
curl -X GET \
  "https://studio-gateway.zerocall.kr/message/send-result/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "x-api-key: zpa.123.your-secret-key"
json
// Response result
{
  "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "content": "안녕하세요. 예약이 확정되었습니다.",
  "subject": "예약 확정 안내",
  "imageList": [],
  "externalMessageId": "biz_1234567890",
  "fromPhoneNumber": "07012345678",
  "toPhoneNumber": "01098765432",
  "sendStatus": "SUCCESS",
  "chatType": "SMS",
  "resultStatus": "4100",
  "resultMessage": "전달",
  "createdAt": "2024-03-05T10:30:05.000Z"
}

필드 설명

필드타입설명
idstring메시지 ID (32자)
contentstring메시지 내용
subjectstring제목. 없으면 필드가 생략될 수 있음
imageListstring[]이미지 URL 목록 (MMS인 경우)
externalMessageIdstring메시지 사업자 측 외부 메시지 ID
fromPhoneNumberstring발신 번호
toPhoneNumberstring수신 번호
sendStatusSUCCESS | FAILEDZeroCall이 메시지 사업자에게 요청을 넘긴 상태
chatTypeSMS | LMS | MMS | RCS | KAKAO메시지 타입
resultStatusstring최종 전달 결과 코드
resultMessagestring결과 코드의 한글 메시지
createdAtstring메시지 생성 시각 (ISO 8601)

최종 발송 성공 여부는 resultStatus 4100,6600,7000 중 하나인지로 판단하세요.

/message/send 요청 실패 시 에러 코드

HTTP코드설명
400Validation Error요청 형식 오류. 전화번호 형식, 본문 길이, 제목 길이, imageList 항목 길이 등을 확인하세요.
404CH001발신에 사용할 채널을 찾을 수 없습니다.
503CH002문자 발송에 실패했습니다.

통화 기록

그룹 단위 통화 기록 목록과 단건 상세 정보를 조회할 수 있습니다.

통화 기록 목록 조회

오프셋 기반 페이지네이션으로 통화 기록을 조회합니다.

GET/call-history

Query Parameters

파라미터타입필수설명
groupIdnumber필수그룹 ID
channelIdnumber선택채널 ID (미입력 시 그룹 전체)
offsetnumber선택시작 인덱스 (기본값: 0)
limitnumber선택조회 개수 (기본값: 20, 최대: 100)
http
GET /call-history?groupId=1&offset=0&limit=20
json
// Response
{
  "data": [
    {
      "callHistoryId": "8f3f8fd9-7ab8-4a26-b523-4f8eb7d38b85",
      "fromNumber": "01012345678",
      "toNumber": "07012345678",
      "isInbound": true,
      "status": "ANSWERED",
      "tag": "AI_TO_DIRECT",
      "duration": 96,
      "summary": "예약 변경 요청 후 담당자 연결하여 상담 완료",
      "sttAI": [
        { "role": "agent", "text": "안녕하세요. 제로콜입니다. 무엇을 도와드릴까요?", "timestamp": 1.2 },
        { "role": "customer", "text": "내일 예약 시간을 변경하고 싶은데요.", "timestamp": 4.3 },
        { "role": "agent", "text": "네, 예약 변경 도와드리겠습니다. 원하시는 시간대가 있으신가요?", "timestamp": 7.1 },
        { "role": "customer", "text": "오후 3시로 바꿀 수 있을까요? 그리고 담당자랑 직접 통화하고 싶어요.", "timestamp": 11.5 },
        { "role": "agent", "text": "네, 담당자에게 연결해드리겠습니다. 잠시만 기다려주세요.", "timestamp": 16.2 }
      ],
      "sttDirect": [
        { "role": "agent", "text": "안녕하세요, 담당자 김민수입니다.", "timestamp": 1.2 },
        { "role": "customer", "text": "네, 내일 오후 3시로 예약 변경하고 싶어서요.", "timestamp": 4.5 },
        { "role": "agent", "text": "확인해보겠습니다. 오후 3시 자리 있네요. 변경 도와드릴게요.", "timestamp": 9.0 },
        { "role": "customer", "text": "감사합니다.", "timestamp": 14.3 }
      ],
      "recordingAI": {
        "url": "https://storage.example.com/recording-ai.wav",
        "expiresAt": "2026-03-12T03:40:00.000Z",
        "contentLength": 1234567,
        "contentType": "audio/wav"
      },
      "recordingDirect": {
        "url": "https://storage.example.com/recording-direct.wav",
        "expiresAt": "2026-03-12T03:40:00.000Z",
        "contentLength": 2345678,
        "contentType": "audio/wav"
      },
      "channelId": 100,
      "groupId": 1,
      "createdAt": "2026-03-12T02:40:00.000Z"
    }
  ],
  "total": 231,
  "offset": 0,
  "limit": 20
}

통화 기록 상세 조회

특정 통화 기록 ID 기준으로 상세 정보를 조회합니다.

GET/call-history/{callHistoryId}

Path Parameters

파라미터타입설명
callHistoryIdstring통화 기록 ID

Query Parameters

파라미터타입필수설명
groupIdnumber필수그룹 ID
http
GET /call-history/{callHistoryId}?groupId=1

status / tag 값

필드가능 값
statusANSWERED`, `MISSED`, `REJECTED`, `UNKNOWN
tagAI`, `DIRECT`, `AI_TO_DIRECT`, `UNKNOWN

녹음 파일 (recordingAI / recordingDirect)

응답 완료된 통화(status: ANSWERED)의 경우, 통화 유형에 따라 녹음 파일 정보가 포함됩니다.

필드타입설명
urlstringS3 Presigned URL (재생/다운로드용)
expiresAtstringURL 만료 시각 (ISO 8601, 1시간)
contentLengthnumber파일 크기 (bytes)
contentTypestringContent-Type (audio/wav)

녹음 파일 URL은 1시간 후 만료됩니다. 파일을 보관해야 하는 경우, URL 수신 후 다운로드하여 별도 저장소에 저장하시기 바랍니다.

웹훅

이벤트 발생 시 등록된 URL로 HTTP POST 요청을 전송합니다.

이벤트설명
MESSAGE_RECEIVED고객으로부터 메시지 수신
SEND_RESULT메시지 발송 결과 수신
CALL_COMPLETED통화 종료 후 분석 결과 수신

MESSAGE_RECEIVED

고객으로부터 메시지를 수신했을 때 전송됩니다.

json
{
  "eventType": "MESSAGE_RECEIVED",
  "timestamp": "2024-03-05T10:30:00.000Z",
  "data": {
    "chatId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "chatRoomId": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
    "groupId": 1,
    "channelId": 100,
    "phoneNumber": "07012345678",
    "senderPhoneNumber": "01098765432",
    "content": "안녕하세요, 예약 문의드립니다.",
    "imageList": [],
    "receivedAt": "2024-03-05T10:30:00.000Z"
  }
}

필드 설명

필드타입설명
data.chatIdstring메시지 고유 ID (32자)
data.chatRoomIdstring채팅방 ID (30자)
data.groupIdnumber그룹 ID
data.channelIdnumber채널 ID
data.phoneNumberstring수신 번호 (ZeroCall 번호)
data.senderPhoneNumberstring발신자 번호 (고객)
data.contentstring메시지 내용
data.subjectstring제목. 없으면 필드가 생략될 수 있음
data.imageListstring[]이미지 URL (MMS)
data.receivedAtstring수신 시각 (ISO 8601)

이미지 URL 만료 안내

imageList에 포함된 이미지 URL은 일정 시간이 지나면 만료됩니다. 이미지를 보관해야 하는 경우, 웹훅 수신 즉시 다운로드하여 별도 저장소에 저장하시기 바랍니다.

SEND_RESULT

메시지 사업자가 최종 발송 결과를 회신했을 때 전송됩니다.

data.chatId/message/sendresult로 받은 32자 문자열과 동일합니다.

아래 값들은 /message/sendresult에는 포함되지 않고,SEND_RESULT 웹훅의data 안에서 내려옵니다.

json
{
  "eventType": "SEND_RESULT",
  "timestamp": "2024-03-05T10:30:05.000Z",
  "data": {
    "chatId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "chatRoomId": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
    "groupId": 1,
    "channelId": 100,
    "sendPhoneNumber": "07012345678",
    "recvPhoneNumber": "01098765432",
    "content": "예약이 확정되었습니다.",
    "chatType": "SMS",
    "sendStatus": "SUCCESS",
    "resultStatus": "4100",
    "resultMessage": "전달",
    "sentAt": "2024-03-05T10:30:05.000Z"
  }
}

필드 설명

필드타입설명
data.chatIdstring메시지 고유 ID (32자)
data.chatRoomIdstring채팅방 ID (30자)
data.groupIdnumber그룹 ID
data.channelIdnumber채널 ID
data.sendPhoneNumberstring발신 번호
data.recvPhoneNumberstring수신 번호
data.contentstring메시지 내용
data.chatTypeSMS | LMS | MMS | RCS | KAKAO메시지 타입
data.sendStatusSUCCESS | FAILEDZeroCall이 메시지 사업자에게 요청을 넘긴 상태
data.resultStatusstring최종 전달 결과 코드
data.resultMessagestring결과 코드의 한글 메시지
data.sentAtstring메시지 생성 시각 (ISO 8601)

sendStatus 값

설명
SUCCESSZeroCall이 메시지 사업자에게 요청을 넘김
FAILED메시지 사업자 요청 단계에서 실패

chatType 값

설명
SMS단문 메시지
LMS장문 메시지
MMS멀티미디어 메시지
RCSRCS 메시지
KAKAO카카오톡 메시지

최종 발송 결과 성공 코드

구분성공 코드설명
SMS4100전달
LMS/MMS6600전달
카카오7000전달

정리하면 /message/send는 즉시 code: "0",message: "Success", 그리고 result에 32자chatId를 반환하고, 최종 성공 여부는 나중에 오는 SEND_RESULT 웹훅의 resultStatus4100,6600,7000 중 하나인지로 판단하세요.

CALL_COMPLETED

통화 종료 후 STT/요약 분석이 완료되면 전송됩니다.

json
{
  "eventType": "CALL_COMPLETED",
  "timestamp": "2026-03-12T02:45:30.000Z",
  "data": {
    "callHistoryId": "8f3f8fd9-7ab8-4a26-b523-4f8eb7d38b85",
    "groupId": 1,
    "channelId": 100,
    "fromNumber": "01012345678",
    "toNumber": "07012345678",
    "isInbound": true,
    "status": "ANSWERED",
    "tag": "AI_TO_DIRECT",
    "duration": 96,
    "summary": "예약 변경 요청을 접수하고 변경 가능한 시간대를 안내함",
    "sttAI": [
      { "role": "agent", "text": "안녕하세요. 제로콜입니다.", "timestamp": 1.2 }
    ],
    "sttDirect": [
      { "role": "customer", "text": "직원 연결 부탁드립니다.", "timestamp": 30.1 }
    ],
    "recordingUrl": "https://storage.example.com/recording.wav",
    "startedAt": "2026-03-12T02:40:00.000Z",
    "completedAt": "2026-03-12T02:41:36.000Z"
  }
}

필드 설명

필드타입설명
data.callHistoryIdstring통화 기록 ID
data.groupIdnumber그룹 ID
data.channelIdnumber채널 ID
data.fromNumberstring발신 전화번호
data.toNumberstring수신 전화번호
data.isInboundboolean수신 통화 여부
data.statusstring통화 상태 (`ANSWERED`/`MISSED`/`REJECTED`/`UNKNOWN`)
data.tagstring통화 태그 (`AI`/`DIRECT`/`AI_TO_DIRECT`/`UNKNOWN`)
data.durationnumber통화 시간(초)
data.summarystring통화 요약
data.sttAIobject[]AI 구간 STT (timestamp: 초 단위, 소수점 포함)
data.sttDirectobject[]Direct 구간 STT (timestamp: 초 단위, 소수점 포함)
data.recordingUrlstring녹음 파일 URL (선택)
data.startedAtstring통화 시작 시각 (ISO 8601)
data.completedAtstring통화 완료 시각 (ISO 8601)

웹훅 처리 권장사항

  • • Timeout: 10초
  • • HTTP 2xx 응답 시 성공 처리
  • • 비동기 처리 후 즉시 응답 권장

에러 처리

API 요청 실패 시 아래 형식으로 에러가 반환됩니다.

json
{
  "status": 401,
  "code": "AU002",
  "message": "올바르지 않은 토큰입니다."
}

HTTP 상태 코드

코드설명
400잘못된 요청 (파라미터 오류)
401인증 실패 (API Key 없음/무효)
403권한 없음 (그룹 접근 불가)
404리소스 없음
429Rate Limit 초과
500서버 오류

메시지 발송 결과 코드

`resultStatus`는 결과 코드이며, `resultMessage`는 해당 코드의 한글 메시지입니다. 성공 판정은 아래 성공 코드 기준으로 처리하세요.

성공 판정 규칙

항목
successCodes4100, 6600, 7000
successMessage전달

SMS 결과 코드

resultStatusresultMessage
4100전달
4400음영 지역
4401단말기 전원 꺼짐
4402단말기 메시지 저장 초과
4403메시지 삭제 됨
4404가입자 위치 정보 없음
4405단말기 BUSY
4410잘못된 번호
4411NPDB 에러
4412착신거절
4420기타에러
4430스팸
4431발송 제한 수신거부(스팸)

LMS/MMS 결과 코드

resultStatusresultMessage
6600전달
6601폰 꺼짐
6602음영지역
6603단말기 문제
6604메시지 삭제 됨
6605단말기 메시지 저장 개수 초과
6610잘못된 번호
6611스팸 차단
6612Spam Content
6613형식불일치
6614MMS 미가입자
6615기타에러
6616통신사 시스템 에러
6617FDS 차단
6618발송불가 시간
6619MESSAGE RATE EXCEED
6620서비스 불가 단말
6621헤더오류
6622잘못된 수신번호
6623메시지 길이 오류
6624중복메시지
6625스팸 차단
6626첨부파일 문제
6627미지원 단말
6628이통사 Timeout

카카오(알림톡/친구톡) 결과 코드

resultStatusresultMessage
7000전달
7001발송 권한 없음
7002메시지 형식 오류
7003메시지 내용 오류
7004메시지 길이 초과
7005메시지 버튼 오류
7006수신자 번호 오류
7007발신프로필키 오류
7008발신프로필 접근 권한 없음
7009발신프로필 상태 오류
7010템플릿 없음
7011등록되지 않은 템플릿
7012검수 중인 템플릿
7013반려된 템플릿
7014승인되지 않은 템플릿
7015휴면 템플릿
7016삭제된 템플릿
7017차단된 수신자
7018수신자 스팸차단
7019수신자 번호 없음
7020대체문자 발송 실패
7021대체문자 형식 오류
7022친구톡 발송 불가 시간
7023친구톡 템플릿 없음
7999기타 오류

코드 예시

cURL

bash
# 그룹 목록 조회
curl -X GET "https://studio-gateway.zerocall.kr/group" \
  -H "x-api-key: zpa.123.your-secret-key"

# 메시지 발송
curl -X POST "https://studio-gateway.zerocall.kr/message/send" \
  -H "x-api-key: zpa.123.your-secret-key" \
  -H "Content-Type: application/json" \
  -d '{
    "toPhoneNumber": "01098765432",
    "content": "안녕하세요. 예약이 확정되었습니다."
  }'

# 통화 기록 조회
curl -X GET "https://studio-gateway.zerocall.kr/call-history?groupId=1&offset=0&limit=20" \
  -H "x-api-key: zpa.123.your-secret-key"

JavaScript

javascript
const axios = require('axios');

const client = axios.create({
  baseURL: 'https://studio-gateway.zerocall.kr',
  headers: {
    'x-api-key': 'zpa.123.your-secret-key',
    'Content-Type': 'application/json'
  }
});

// 메시지 발송
async function sendMessage(to, content) {
  const response = await client.post('/message/send', {
    toPhoneNumber: to,
    content: content
  });
  return response.data.result; // "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

// 그룹 목록 조회
async function getGroups() {
  const response = await client.get('/group');
  return response.data;
}

// 통화 기록 조회
async function getCallHistories(groupId) {
  const response = await client.get('/call-history', {
    params: { groupId, offset: 0, limit: 20 }
  });
  return response.data;
}

Python

python
import requests

BASE_URL = 'https://studio-gateway.zerocall.kr'
API_KEY = 'zpa.123.your-secret-key'

headers = {
    'x-api-key': API_KEY,
    'Content-Type': 'application/json'
}

# 메시지 발송
def send_message(to: str, content: str) -> str:
    response = requests.post(
        f'{BASE_URL}/message/send',
        headers=headers,
        json={
            'toPhoneNumber': to,
            'content': content
        }
    )
    return response.json()['result']  # "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# 그룹 목록 조회
def get_groups() -> list:
    response = requests.get(f'{BASE_URL}/group', headers=headers)
    return response.json()

# 통화 기록 조회
def get_call_histories(group_id: int) -> dict:
    response = requests.get(
        f'{BASE_URL}/call-history',
        headers=headers,
        params={'groupId': group_id, 'offset': 0, 'limit': 20}
    )
    return response.json()

웹훅 수신 (Node.js)

javascript
const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhook', (req, res) => {
  const { eventType, data } = req.body;

  switch (eventType) {
    case 'MESSAGE_RECEIVED':
      // 메시지 수신 처리
      console.log('메시지 수신:', data.content);
      handleIncomingMessage(data);
      break;
    case 'SEND_RESULT':
      // 발송 결과 처리
      console.log('chatId:', data.chatId);
      console.log('최종 결과:', data.resultStatus, data.resultMessage);
      handleSendResult(data);
      break;
    case 'CALL_COMPLETED':
      // 통화 종료 처리
      console.log('통화 완료:', data.callHistoryId, data.status, data.tag);
      handleCallCompleted(data);
      break;
  }

  // 즉시 200 응답 (비동기 처리 권장)
  res.status(200).send('OK');
});

app.listen(3000);