에러 코드

에러 응답 형태 · HTTP 상태 코드 요약 · 전체 에러 코드 표.

요청의 성패는 HTTP 상태 코드로 알립니다. 2xx는 성공, 4xx는 보낸 정보로는 처리할 수 없었다는 뜻이고(대부분 error 코드가 함께 옵니다), 5xx는 우리 쪽 문제이거나 통신사·LiveKit 같은 외부 의존의 문제입니다.

에러 응답 형태

오류 본문은 평평한 봉투입니다. 코드는 항상 error 키에 있고, 진단에 필요한 값이 있으면 중첩 없이 같은 층에 함께 옵니다.

429 Too Many Requests
{ "error": "number_busy", "current": 1, "limit": 1 }

같은 층에 올 수 있는 키는 다음이 전부입니다. 어떤 코드에 무엇이 붙는지는 아래 에러 코드 표의 설명에 적혀 있습니다.

타입언제
errorstring항상. 아래 표의 코드 중 하나입니다.
fieldstring필수 값이 빠졌을 때 그 필드 이름.
supportedstring[]값이 허용 목록 밖일 때 그 목록.
current · limitnumber동시통화 한도에 걸렸을 때 현재값과 한도.
end_reasonstring실제로 다이얼을 시도한 뒤 실패했을 때. 값의 뜻은 통화 객체의 종료 사유와 같습니다.
reasonstring녹음 스트림·링크를 못 준 이유 (pending · none · failed).
statusstring종료·옵션 변경을 거절했을 때 그 통화의 현재 상태.
candidatesobject[]발신 번호를 골라야 할 때 후보 목록 (number_id · phone · workspace_name).

/api/my/*조회 실패 404만 FastAPI 기본 봉투({"detail": "call not found"})를 씁니다. 같은 라우터라도 녹음 링크 거절은 error 봉투이고, /api/v1/*는 전부 error입니다. 중첩된 error.code 구조는 어디에도 없습니다.

HTTP 상태 코드

코드
200요청이 처리됐습니다.
201발신을 접수했습니다 — 통화는 비동기로 진행됩니다.
202종료를 요청했습니다. 실제 종료는 작별 인사가 끝난 뒤입니다.
206요청한 녹음 파일의 일부 바이트를 반환했습니다.
400본문이나 값이 잘못됐습니다. 그대로 재시도하면 같은 결과입니다.
401자격증명이 없거나 무효합니다.
403자격증명은 유효하지만 그 자원에 권한이 없습니다.
404없거나, 내 것이 아닙니다 — 둘을 구분해 알려주지 않습니다.
409지금 상태에서는 할 수 없는 요청입니다. 재시도해도 대개 같습니다.
416요청한 녹음 바이트 범위가 파일 범위를 벗어났습니다.
422값은 형식에 맞지만 설정·문맥과 맞지 않습니다.
429한도를 넘었습니다. 잠시 뒤 재시도하세요.
502외부 의존(LiveKit 등)이 일시적으로 실패했습니다 — 재시도할 만합니다.
503그 기능이 이 배포에 설정돼 있지 않거나 회선을 잡지 못했습니다.

발신 거절은 대부분 전화를 걸기 전에 납니다 — 그 경우 통화 기록도 요금도 생기지 않습니다. 실제로 다이얼한 뒤의 실패에는 end_reason이 함께 옵니다.

에러 코드

REST와 MCP가 같은 코드 집합을 씁니다. 알파벳순입니다.

errorHTTP
call_not_active409끊거나 옵션을 바꾸려는 통화가 진행 중이 아닙니다. 이미 끝났거나 아직 연결 전입니다 — 현재 status가 함께 옵니다.
call_not_found404그 통화가 없거나 내 번호의 통화가 아닙니다. 둘을 구분해 알려주지 않습니다(존재 은닉).
concurrency_limit_platform_exceeded429플랫폼 전체 동시통화 한도를 넘었습니다. 잠시 뒤 재시도하세요.
concurrency_limit_workspace_exceeded429워크스페이스 동시통화 한도를 넘었습니다.
dispatch_failed503다이얼했지만 그 밖의 이유로 회선을 잡지 못했습니다. 실제로 시도한 결과라 end_reason이 함께 옵니다.
from_number_required422발신 번호를 지정하지 않았는데 접근 가능한 번호가 둘 이상입니다. candidates에서 하나를 골라 다시 부르세요. OAuth 연결에서만 납니다.
hangup_failed502종료 신호 전송이 일시적으로 실패했습니다 — 재시도할 만합니다.
hangup_unavailable503이 배포에 종료 기능이 설정돼 있지 않습니다.
invalid_api_key401Authorization 헤더가 없거나 Bearer가 아니거나 모르는 키입니다. 키는 재발급하면 이전 키가 즉시 무효가 됩니다.
invalid_language400language가 ko · vi · en · zh · es 중 하나가 아닙니다. 지원 목록이 supported로 함께 옵니다.
invalid_max_duration_sec400max_duration_sec가 gateway의 CallPolicy 검증을 통과하지 못했습니다.
invalid_phone_format400착신번호가 국내형 010/070(11자리)도, 짧은 내선도 아닙니다. 국제번호는 지원하지 않습니다.
invalid_request_body400본문이 JSON이 아니거나 형식이 맞지 않습니다. 통화 옵션 변경에서는 아는 옵션이 하나도 없거나, 값의 타입이 다르거나, 허용 밖 값일 때(예: 지원하지 않는 language) 나며, 지원 목록이 supported로 함께 옵니다.
language_unavailable422통화 중 언어 전환 요청인데, 이 통화가 쓰는 TTS 프로바이더가 그 언어를 말할 수 없습니다. 통화 중에는 프로바이더를 바꿀 수 없어 전환만 거절되고 통화는 그대로 진행됩니다. 현재 프로바이더가 provider로 함께 옵니다.
missing_required_field400필수 값이 비어 있습니다. 어느 필드인지가 field로 함께 옵니다(현재는 to).
max_duration_exceeds_policy400max_duration_sec가 이 번호에 적용되는 최대 통화 시간보다 깁니다. 현재 허용 상한이 maximum으로 함께 옵니다.
no_agent_endpoint422자체 Agent를 바인딩했는데 chat URL이 비어 있습니다. 전화를 걸지 않고 거절합니다.
no_number_available422발신 가능한(배정된) 번호가 하나도 없습니다. OAuth 연결에서만 납니다.
not_configured503이 배포에 녹음 재생 링크 기능이 켜져 있지 않습니다.
number_busy429이 번호의 동시통화 한도를 넘었거나(current·limit 동반), 다이얼했더니 상대가 통화 중이었습니다(SIP 486 — end_reason 동반). 두 경우를 함께 오는 키로 가릅니다.
number_not_allowed403지정한 번호에 접근 권한이 없습니다. OAuth 연결에서만 납니다.
number_not_found404다이얼했더니 없는 번호였습니다 (SIP 404). end_reason이 함께 옵니다.
number_required422from_number_required와 같은 상황을 MCP end_call 도구가 부르는 이름입니다 — 그 도구의 인자 이름이 number이기 때문입니다.
options_failed502통화 옵션 변경 신호를 보내지 못했습니다 — 일시적이라 재시도할 수 있습니다.
options_unavailable503통화 옵션 기능이 이 배포에 설정돼 있지 않습니다.
range_not_satisfiable416요청한 녹음 바이트 범위가 파일 범위를 벗어났습니다. Range 값을 다시 계산하세요.
recording_not_available409녹음이 available이 아닙니다. 어느 상태인지가 reason으로 함께 옵니다 — pending이면 마감 중이라 몇 초 뒤 되고, none이면 녹음하지 않은 통화, failed면 재시도해도 같습니다. v1 직접 스트림과 녹음 링크가 함께 씁니다.
recording_storage_error502완료된 녹음 파일을 저장소에서 읽지 못했습니다. 일시적일 수 있으므로 재시도하세요.
recording_unavailable503이 배포에 v1 녹음 직접 스트리밍 기능이 설정돼 있지 않습니다.
room_unknown409종료·옵션 변경 신호를 보낼 통화 세션을 찾지 못했습니다. 수신 통화에서 gateway가 재시작된 뒤 납니다.
unauthorized401MCP 엔드포인트에 자격증명 없이 접근했거나 OAuth 토큰이 무효합니다.
workspace_inactive403번호가 워크스페이스에 배정돼 있지 않거나 워크스페이스가 비활성 상태입니다.
  • 종료·옵션 변경 거절은 통화를 죽이지 않습니다. call_not_active · room_unknown · hangup_failed · options_failed 어느 쪽이든 통화는 살아 있으니, 사유를 받고 대화를 이어가세요.
  • 202접수이지 적용이 아닙니다 — 통화 옵션은 상태 조회 options_pending으로 반영을 확인하세요.
  • 통화가 어떻게 끝났는지는 오류가 아니라 통화 객체의 값입니다 — Calls API › 통화 객체 end_reason를 보세요.

엔드포인트별로 어떤 코드가 나는지는 각 엔드포인트 절에 적지 않습니다 — 코드가 늘 때마다 두 곳을 고쳐야 하고, 실제로 한쪽이 부분집합인 채로 남은 적이 있습니다.