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

Status Callback

통화 상태 변경 시 전송되는 Status Callback의 이벤트 종류와 파라미터를 안내합니다.

Status Callback은 통화 상태가 변경될 때마다 지정한 URL로 이벤트를 전송합니다.

  • 발신(outbound): 통화 생성 시 status_callback 파라미터로 통화마다 설정합니다.
  • 수신(inbound): 외부에서 걸려오는 통화는 발신 시점이 없으므로, 전화번호 설정의 "수신 통화 상태 webhook"(status_callback)에 URL을 미리 등록합니다. 대시보드 전화번호 상세 또는 번호 수정 API의 status_callback 으로 설정하며, 그 번호로 걸려오는 모든 통화의 상태 전이가 통지됩니다.
call = client.calls.create(
    to="01012345678",
    from_="07052358010",
    url="https://my-app.com/twiml",
    status_callback="https://my-app.com/status",
    status_callback_event="initiated ringing answered completed",
)

이벤트 종류

status_callback_event에 수신할 이벤트를 공백으로 구분하여 지정합니다.

파라미터타입필수설명
initiatedevent선택통화가 시작됨 (발신 시작)
ringingevent선택상대방 전화벨이 울리는 중
answeredevent선택상대방이 전화를 받음
completedevent선택통화가 종료됨
transferevent선택호전환 진행 상황 — 전환 시작·연결·종료마다 전송.

지정하지 않으면 initiated ringing answered completed 가 적용됩니다. transfer 는 여기에 포함되지 않으므로, 호전환 통지를 받으려면 네 개와 함께 직접 나열해야 합니다.

initiated ringing answered completed transfer

대시보드에서는 전화번호 상세 → 인바운드 라우팅 → 수정에서 체크박스로 켤 수 있습니다.

호전환 이벤트 (transfer)

호전환이 어떻게 됐는지 통화가 끝나기 전에 알 수 있습니다. 전환이 실패하면 다른 상담원으로 재시도하거나 안내 문자를 보내는 식으로 바로 이어갈 수 있습니다.

전환 한 건마다 시작(initiated) → 연결(connected) → 종료(completed / failed / no-answer / busy / canceled) 순으로 전송됩니다. 연결되지 못하면 connected 없이 시작과 종료만 옵니다.

파라미터타입필수설명
TransferStatusstring필수initiated(전환 시작) / connected(상대 응답, 통화 연결) / completed(정상 종료) / failed / no-answer / busy / canceled / interrupted(통화가 비정상 종료돼 시스템이 정리)
TransferSequencestring필수이 통화의 몇 번째 전환 시도인지 (1부터). 다단 전환에서 어느 시도의 통지인지 구분합니다
TransferTostring선택전환 대상 번호
TransferModestring선택전환 방식
TransferDurationstring선택전환 통화 시간(초) — 종료 이벤트에만 포함
TransferReasonstring선택에이전트가 파악한 문의 유형. 파악하지 못했으면 파라미터 자체가 오지 않습니다
TransferContextstring선택Voice Agent SDK 의 transfer({ context }) 로 직접 넘긴 구조화 데이터(JSON 문자열). 넘긴 그대로 전달되며, 넘기지 않았으면 파라미터 자체가 오지 않습니다
Fromstring선택통화의 발신번호
Tostring선택통화의 수신번호
Directionstring선택inbound / outbound — From·To 중 어느 쪽이 고객인지 판단하는 근거입니다

이 이벤트는 아래 "요청 파라미터" 를 따르지 않습니다. CallId · AccountId · Timestamp · From · To · Direction 과 위 Transfer* 필드만 실립니다 — CallStatus없습니다. 통화 자체의 상태 통지와 구분하려면 TransferStatus 필드의 존재 여부로 분기하세요.

TransferReasonTransferContext출처가 다릅니다. 앞은 에이전트가 통화 중 파악한 내용이고, 뒤는 SDK 로 직접 넘긴 값이 그대로 전달된 것입니다. 둘 다 값이 없으면 파라미터 자체가 오지 않으므로, 읽기 전에 존재 여부를 확인하세요.

같은 종료 이벤트가 두 번 이상 도착할 수 있습니다(전환 종료 처리가 여러 경로에서 겹칠 때). CallId + TransferSequence + TransferStatus 조합을 키로 삼아 멱등하게 처리하세요.

요청 파라미터

기본 파라미터(CallId, AccountId, Direction)에 추가로 다음 파라미터가 포함됩니다.

파라미터타입필수설명
CallStatusstring필수현재 통화 상태. 진행: initiated/ringing/answered. 종료(completed 이벤트의 실제 사유): completed/busy/no-answer/failed/canceled/rejected. rejected/busy 는 통화가 연결되지 않고 거절된 경우이며, 이때 Duration 은 0 입니다.
Fromstring필수발신번호
Tostring필수수신번호
ForwardedFromstring선택착신전환으로 들어온 수신 통화에서, 발신자가 원래 건 번호. 여러 번호를 ClawOps 번호 하나로 착신전환했을 때 어느 번호로 걸려온 전화인지 구분합니다. 착신전환을 실행한 통신사가 전환 정보를 함께 보내온 경우에만 실리며, 직접 걸려온 통화와 발신 통화에는 파라미터 자체가 오지 않습니다
Durationstring선택통화 시간 (초) — completed 이벤트에서만 포함
Timestampstring필수이벤트 발생 시각 (ISO 8601)
Directionstring필수inbound(수신) 또는 outbound(발신)
AnsweredBystring선택AMD(자동응답기 감지) 결과 — machine_detection 을 켠 발신 통화의 completed 이벤트에만 포함. human(사람) / machine(자동응답기·음성사서함) / unknown(판정 불가)
HangupCausestring선택통화 종료 사유 — 종료 이벤트에만 포함. invalid_number(결번) / no_answer / user_busy / network_out_of_order 등. 전체 목록은 아래 참고
HangupCauseQ850string선택통신망 Q.850 cause code (1=결번, 16=정상해제, 17=통화중, 19=무응답, 38=망장애 등)
SipResponseCodestring선택종료를 유발한 SIP 응답코드 (404=없는 번호, 486=통화중, 500=망 오류 등). 응답코드 없이 끝난 통화는 미포함
HangupSourcestring선택종료 책임 주체 — carrier(통신망) / callee(수신자) / caller(발신자) / app·system(ClawOps 측 오류)
ParentCallIdstring선택원래 통화의 ID — <Dial> 로 연결된 상대(수신 leg)의 통지에만 포함됩니다. 이때 CallId 는 그 연결만의 ID 이고, From/To 는 그 연결의 발신·수신번호입니다

<Dial> 로 연결한 상대의 상태도 따로 받을 수 있습니다 — <Number> / <Sip>statusCallback 을 지정하면 그 목적지가 받은 시점(answered)과 끝난 시점(completed)이 통지됩니다. 이 통지에는 ParentCallId 가 함께 실려 어느 통화의 연결인지 구분됩니다. 자세한 내용은 VoiceML <Number> 를 참고하세요.

CallStatus: "failed" 는 결번·망 오류·시스템 오류를 모두 포함하는 대분류입니다. 결번인지 일시적 오류인지는 HangupCause 로 구분하세요 — 통화 실패 사유 에 전체 목록과 재시도 판단 기준이 있습니다.

종료 사유 필드들은 통화가 끝난 이벤트(completed / busy / no-answer / failed / canceled / rejected)에만 포함됩니다. 사유를 확정하지 못한 통화에서는 필드가 생략됩니다 — 값이 있을 때만 보내며, 없는 사유를 임의로 채우지 않습니다.

예제

from flask import Flask, request

app = Flask(__name__)

@app.route("/status", methods=["POST"])
def status_callback():
    call_id = request.form.get("CallId")
    status = request.form.get("CallStatus")
    duration = request.form.get("Duration")

    print(f"통화 {call_id}: {status}")
    if status == "completed" and duration:
        print(f"  통화 시간: {duration}초")

    # 연결되지 않은 통화는 사유를 보고 재시도 여부를 정합니다.
    if status == "failed":
        cause = request.form.get("HangupCause")
        if cause == "invalid_number":
            print("  결번 — 목록에서 제외")
        elif request.form.get("HangupSource") in ("app", "system"):
            print("  ClawOps 측 오류 — 재시도")

    return "", 204

Status Callback 요청에도 X-Signature 헤더가 포함됩니다. 서명 검증 가이드를 참고하여 요청의 무결성을 확인하세요.