새로운 문서를 작성 중입니다.새 문서로 이동
ClawOps Docs

통화 실패 사유 (Hangup Cause)

통화가 왜 끝났는지를 나타내는 hangupCause·hangupCauseQ850·sipResponseCode·hangupSource 전체 목록. 결번(없는 번호) 자동 정제, 재시도 판단, 실패 분석에 사용합니다.

통화 실패 사유 (Hangup Cause)

status 는 통화가 어떻게 끝났는지(completed / failed / busy / no-answer ...)만 알려줍니다. 그렇게 끝났는지 — 결번인지, 상대가 안 받은 건지, 망 장애인지 — 는 hangupCause 로 구분합니다.

특히 failed 는 결번·망 오류·시스템 오류를 모두 포함하는 대분류입니다. 발신 리스트를 정제하거나 재시도 여부를 판단하려면 status 가 아니라 hangupCause 를 보세요.

어디서 받나요

경로필드
통화 단건 조회hangupCause · hangupCauseQ850 · sipResponseCode · hangupSource
통화 목록 조회위와 동일 (통화마다)
Status Callback webhookHangupCause · HangupCauseQ850 · SipResponseCode · HangupSource (종료 이벤트)
{
  "callId": "CAabcdef1234567890",
  "status": "failed",
  "to": "07080588491",
  "duration": 0,
  "hangupCause": "invalid_number",
  "hangupCauseQ850": 1,
  "sipResponseCode": 404,
  "hangupSource": "carrier"
}

네 값은 같은 사건을 서로 다른 해상도로 표현합니다. 보통은 hangupCause 하나면 충분하고, hangupCauseQ850·sipResponseCode 는 통신망 원본 신호가 필요한 정밀 분석용입니다.

재시도 판단

가장 흔한 용도입니다. 이 표만 보고 분기하면 됩니다.

재시도해도 소용없음 — 번호를 목록에서 제외하세요

hangupCauseQ.850
invalid_number1 · 5 · 28결번. 없는 번호이거나 형식이 잘못됐습니다
number_changed22번호가 변경됐습니다
incompatible_destination88해당 번호로는 음성 통화를 연결할 수 없습니다
recipient_blocked21수신거부 명단에 등록된 번호라 걸지 않았습니다. 벨은 울리지 않았고 요금도 발생하지 않습니다

recipient_blocked 는 통신망이 아니라 여러분이 등록한 명단 때문에 걸리지 않은 통화입니다. 재시도하면 같은 결과이고, 무엇보다 연락하지 말라고 한 상대에게 다시 거는 일이 됩니다. 명단은 수신거부 목록 조회 또는 콘솔의 수신거부 화면에서 관리하고, 해제해야 다시 발신됩니다. 발신 API 호출 자체는 422 recipient_blocked 로 즉시 거절됩니다.

재시도 가치 있음 — 일시적인 사유입니다

hangupCauseQ.850
no_answer18 · 19 · 20벨은 울렸으나 받지 않음
user_busy17통화 중
recovery_on_timer_expire102응답 대기 시간 초과
temporary_failure41통신망 일시 장애
switching_congestion42교환기 혼잡
no_circuit_available34회선 부족
network_out_of_order38망 장애
destination_out_of_order27상대 단말/교환기 장애
resource_unavailable47망 자원 부족
protocol_error111통신망 프로토콜 오류

계정 한도·상태 — 번호가 아니라 계정을 확인하세요

수신 통화가 계정 설정 때문에 연결되지 못한 경우입니다. 상대 번호에는 문제가 없습니다.

hangupCauseQ.850
concurrency_limit_exceeded17동시통화 한도를 넘어 받지 못했습니다. 잠시 후 재시도하거나 동시통화 채널을 올리세요
subscription_inactive21구독이 없거나 만료되어 받지 못했습니다. 재시도해도 동일합니다
inbound_quota_exceeded21월 수신 통화 한도를 다 썼습니다. 다음 청구 주기에 초기화됩니다
inbound_not_supported21현재 플랜이 수신 통화를 지원하지 않습니다
sip_trunk_inactive21SIP 트렁크 부가서비스가 없거나 만료되어 BYOC·소프트폰 번호로 받지 못했습니다

1채널로 운영 중에 두 번째 전화가 들어오면 concurrency_limit_exceeded 로 통화 기록이 남고 Status Callback webhook 이 발송됩니다. 발신번호(From)를 받아 콜백 안내 문자를 보내는 구성에 쓰실 수 있습니다.

ClawOps 측 오류 — 재시도하세요

hangupCausehangupSource
app_errorapp통화 처리 중 오류가 발생했습니다 (VoiceML 파싱 실패 등 포함)
call_stucksystem통화가 비정상 상태로 남아 시스템이 정리했습니다

hangupSourceapp 또는 system 이면 상대방·번호 문제가 아니라 ClawOps 측 문제입니다. 수신자 번호를 정제 대상으로 넣지 마시고 재시도해 주세요. 반복되면 통화 ID 와 함께 문의해 주시면 원인을 확인해 드립니다.

실패가 아닌 종료

hangupCauseQ.850
normal_clearing16통화 후 정상 종료 (status: "completed")
caller_canceled16상대가 받기 전에 발신 측이 끊음
call_rejected21 · 50 · 57수신자가 거절 (수신 차단 포함)
unspecified31 · 127통신망이 사유를 주지 않음
unknown위 어디에도 해당하지 않는 cause

사유로 조회하기

통화 목록 조회hangupCause 로 필터링합니다. statusfailed 하나에 결번·망 오류·시스템 오류가 다 섞여 있어, 사유별로 보려면 이쪽을 쓰세요.

GET /v1/accounts/{accountId}/calls?hangupCause=concurrency_limit_exceeded
GET /v1/accounts/{accountId}/calls?hangupCause=invalid_number&hangupCause=number_changed

기간·통화시간과 함께 좁힐 수도 있습니다. since/until 은 시작 시각 기준이고 until 은 미포함, minDuration/maxDuration 은 초 단위로 양끝을 포함합니다. 통화 시간이 아직 확정되지 않은 통화(진행 중 등)는 minDuration/maxDuration 을 주면 결과에서 빠집니다.

# 지난달 놓친 전화 중 0초로 끝난 것만
GET /v1/accounts/{accountId}/calls?since=2026-08-01T00:00:00Z&until=2026-09-01T00:00:00Z&minDuration=0&maxDuration=0

hangupSource — 누구 때문에 끝났나

carrier통신망이 통화를 끊었습니다 (결번·망 장애 등)
callee수신자 측입니다 (거절·통화중·무응답·통화 후 종료)
caller발신 측입니다 (연결 전 취소)
appClawOps 통화 처리 오류
systemClawOps 시스템이 정리한 통화

sipResponseCode

통신망이 보낸 SIP 응답코드가 있을 때만 채워집니다. 없이 끝난 통화는 null 입니다.

코드흔한 의미
404없는 번호
403발신이 거부됨
408응답 없음
480일시적으로 연결할 수 없음
486통화 중
500통신망 내부 오류 — 실제 사유는 hangupCause 를 보세요

국내 통신망은 실제 사유를 500 으로 감싸 보내는 경우가 있습니다. ClawOps 는 통신망이 함께 보낸 Q.850 원인값을 읽어 hangupCause 로 교정하므로, SIP 코드보다 hangupCause 가 정확합니다.

예제 — 결번 자동 정제

발신 후 통화 결과를 조회해, 다시 걸어도 소용없는 번호를 목록에서 빼는 예제입니다.

DO_NOT_RETRY = {"invalid_number", "number_changed", "incompatible_destination"}

call = client.accounts(account_id).calls.get(call_id)

if call.status != "completed":
    cause = getattr(call, "hangup_cause", None)
    if cause in DO_NOT_RETRY:
        mark_number_invalid(call.to)          # 고객 DB 에서 제외
    elif getattr(call, "hangup_source", None) in ("app", "system"):
        enqueue_retry(call.to)                # ClawOps 측 오류 — 재시도
    else:
        enqueue_retry(call.to, delay_min=30)  # 일시적 사유 — 나중에 재시도

Status Callback 으로 실시간 처리하려면 종료 이벤트에서 같은 값을 읽으면 됩니다.

@app.route("/status", methods=["POST"])
def status_callback():
    if request.form.get("CallStatus") in ("completed", "failed", "busy", "no-answer"):
        cause = request.form.get("HangupCause")
        if cause in DO_NOT_RETRY:
            mark_number_invalid(request.form.get("To"))
    return "", 204

통화 전환(transfer)의 실패 사유

이 페이지의 hangupCause발신자와 ClawOps 사이의 통화(메인 leg) 에 대한 값입니다. 에이전트나 SIP 단말이 상담원에게 넘긴 전환 leg 는 별도 필드를 씁니다 — transferHangupCauseQ850 · transferSipResponseCode · transferReasonText, 그리고 다단계 전환의 전체 이력은 transfers[] 입니다. 값을 읽는 법은 통화 전환 결과 를 보세요.

VoiceML <Dial> 은 이 transfer* 필드를 채우지 않습니다. <Dial> 결과는 action 요청의 DialCallStatus 로 받습니다 — VoiceML 참고.