에러 코드
에러 응답 형태 · HTTP 상태 코드 요약 · 전체 에러 코드 표.
요청의 성패는 HTTP 상태 코드로 알립니다. 2xx는 성공, 4xx는 보낸 정보로는 처리할 수 없었다는
뜻이고(대부분 error 코드가 함께 옵니다), 5xx는 우리 쪽 문제이거나 통신사·LiveKit 같은 외부
의존의 문제입니다.
에러 응답 형태
오류 본문은 평평한 봉투입니다. 코드는 항상 error 키에 있고, 진단에 필요한 값이 있으면 중첩
없이 같은 층에 함께 옵니다.
429 Too Many Requests
{ "error": "number_busy", "current": 1, "limit": 1 }같은 층에 올 수 있는 키는 다음이 전부입니다. 어떤 코드에 무엇이 붙는지는 아래 에러 코드 표의 설명에 적혀 있습니다.
| 키 | 타입 | 언제 |
|---|---|---|
error | string | 항상. 무엇이 잘못됐는지. 값은 상태 코드별 응답 설명에 있습니다. |
candidates | object[] | 발신 번호를 골라야 할 때의 후보 (number_id · phone · workspace_name). |
current | integer | 동시통화 한도에 걸렸을 때 지금 진행 중인 통화 수. |
end_reason | string | 실제로 다이얼을 시도한 뒤 실패했을 때. |
field | string | 필수 값이 빠졌을 때 그 필드 이름. |
language | string | 언어 전환을 거절했을 때 요청받은 언어. |
limit | integer | 그때 걸린 한도 값. |
maximum | integer | 아래 표의 코드 중 하나입니다. |
message | string | 본문 검증에 걸린 첫 항목을 한 줄로 옮긴 것. |
provider | string | 언어 전환을 거절했을 때 그 통화가 쓰는 TTS 프로바이더. |
reason | string | 아래 표의 코드 중 하나입니다. |
status | string | 종료·옵션 변경을 거절했을 때 그 통화의 현재 상태. |
supported | string[] | 값이 허용 목록 밖일 때 그 목록. |
/api/my/*의 조회 실패 404만 FastAPI 기본 봉투({"detail": "call not found"})를 씁니다. 같은
라우터라도 녹음 링크 거절은 error 봉투이고, /api/v1/*는 전부 error입니다. 중첩된 error.code
구조는 어디에도 없습니다.
HTTP 상태 코드
| 코드 | 뜻 |
|---|---|
| 200 | 요청이 처리됐습니다. |
| 201 | 발신을 접수했습니다 — 통화는 비동기로 진행됩니다. |
| 202 | 종료·옵션 변경을 접수했습니다. 실제 반영은 그다음입니다. |
| 400 | 본문이나 값이 잘못됐습니다. 그대로 재시도하면 같은 결과입니다. |
| 401 | 자격증명이 없거나 무효합니다. |
| 403 | 자격증명은 유효하지만 그 자원에 권한이 없습니다. |
| 404 | 없거나, 내 것이 아닙니다 — 둘을 구분해 알려주지 않습니다. |
| 409 | 지금 상태에서는 할 수 없는 요청입니다. 재시도해도 대개 같습니다. |
| 422 | 값은 형식에 맞지만 설정·문맥과 맞지 않습니다. |
| 429 | 한도를 넘었습니다. 잠시 뒤 재시도하세요. |
| 502 | 외부 의존(LiveKit 등)이 일시적으로 실패했습니다 — 재시도할 만합니다. |
| 503 | 그 기능이 이 배포에 설정돼 있지 않거나 회선을 잡지 못했습니다. |
발신 거절은 대부분 전화를 걸기 전에 납니다 — 그 경우 통화 기록도 요금도 생기지 않습니다. 실제로
다이얼한 뒤의 실패에는 end_reason이 함께 옵니다.
에러 코드
/api/v1/*가 낼 수 있는 코드 전부입니다. 알파벳순이고, 각 오퍼레이션 지면의 응답 스키마
(V1Error)에도 같은 목록이 실립니다.
| error | HTTP | 뜻 |
|---|---|---|
call_not_active | 409 | 끊거나 옵션을 바꾸려는 통화가 진행 중이 아닙니다. 이미 끝났거나 아직 연결 전입니다 — 현재 status가 함께 옵니다. |
call_not_found | 404 | 그 통화가 없거나 내 번호의 통화가 아닙니다. 둘을 구분해 알려주지 않습니다. |
concurrency_limit_platform_exceeded | 429 | 플랫폼 전체 동시통화 한도를 넘었습니다. 잠시 뒤 재시도하세요. current·limit이 함께 옵니다. |
concurrency_limit_workspace_exceeded | 429 | 워크스페이스 동시통화 한도를 넘었습니다. current·limit이 함께 옵니다. |
dispatch_failed | 503 | 다이얼했지만 회선을 잡지 못했습니다. 실제로 시도한 결과라 end_reason이 함께 옵니다. |
from_number_required | 422 | 발신 번호를 지정하지 않았는데 접근 가능한 번호가 둘 이상입니다. candidates에서 하나를 골라 X-Number-Id로 다시 부르세요. OAuth 연결에서만 납니다. |
hangup_failed | 502 | 종료 신호 전송이 일시적으로 실패했습니다 — 재시도할 만합니다. 통화는 살아 있습니다. |
hangup_unavailable | 503 | 이 배포에 종료 기능이 설정돼 있지 않습니다. |
invalid_api_key | 401 | Authorization 헤더가 없거나 Bearer가 아니거나 모르는 키입니다. 키를 재발급하면 이전 키는 즉시 무효가 됩니다. |
invalid_language | 400 | 지원하지 않는 통화 언어입니다. 지원 목록이 supported로 함께 옵니다. |
invalid_max_duration_sec | 400 | 최대 연결 시간은 60~1800초여야 합니다. |
invalid_phone_format | 400 | 착신번호가 국내형 010/070(11자리)도, 짧은 내선도 아닙니다. 국제번호는 지원하지 않습니다. |
invalid_request_body | 400 | 본문이 JSON이 아니거나 형식이 맞지 않습니다. 아는 옵션이 하나도 없거나, 값의 타입이 다르거나, 허용 밖 값일 때도 이 코드입니다. 이 엔드포인트가 아는 필드가 supported로, 검증에 걸린 첫 항목이 message로 함께 옵니다. 쿼리 파라미터가 범위를 벗어난 경우도 같습니다. |
language_unavailable | 422 | 통화 중 언어 전환 요청인데, 이 통화가 쓰는 TTS 프로바이더가 그 언어를 말할 수 없습니다. 통화 중에는 프로바이더를 바꿀 수 없어 전환만 거절되고 통화는 그대로 진행됩니다. 현재 프로바이더가 provider로 함께 옵니다. |
max_duration_exceeds_policy | 400 | 번호의 통화 정책 상한을 넘었습니다. maximum 이하로 요청하세요. |
missing_required_field | 400 | 필수 값이 비어 있습니다. 어느 필드인지가 field로 함께 옵니다. |
no_agent_endpoint | 422 | 자체 Agent를 바인딩했는데 chat URL이 비어 있습니다. 전화를 걸지 않고 거절합니다. |
no_number_available | 422 | 발신 가능한(배정된) 번호가 하나도 없습니다. OAuth 연결에서만 납니다. |
number_busy | 429 | 이 번호의 동시통화 한도를 넘었거나(current·limit 동반), 다이얼했더니 상대가 통화 중이었습니다(end_reason 동반). 함께 오는 키로 두 경우를 가릅니다. |
number_not_allowed | 403 | 지정한 번호에 접근 권한이 없습니다. OAuth 연결에서만 납니다. |
number_not_found | 404 | 다이얼했더니 없는 번호였습니다. end_reason이 함께 옵니다. |
options_failed | 502 | 통화 옵션 변경 신호를 보내지 못했습니다 — 일시적이라 재시도할 수 있습니다. 통화는 살아 있습니다. |
options_unavailable | 503 | 통화 옵션 기능이 이 배포에 설정돼 있지 않습니다. |
range_not_satisfiable | 416 | 요청한 녹음 바이트 범위를 제공할 수 없습니다. |
recording_not_available | 409 | 녹음 파일을 받을 수 없습니다. reason은 pending(준비 중), none(없음), failed(실패)입니다. |
recording_storage_error | 502 | 녹음 저장소에서 파일을 가져오지 못했습니다. |
recording_unavailable | 503 | 이 배포에 녹음 저장소가 설정되지 않았습니다. |
room_unknown | 409 | 종료·옵션 변경 신호를 보낼 통화 세션을 찾지 못했습니다. 수신 통화에서 gateway가 재시작된 뒤 납니다. 통화는 살아 있습니다. |
workspace_inactive | 403 | 번호가 워크스페이스에 배정돼 있지 않거나 워크스페이스가 비활성 상태입니다. |
- 종료·옵션 변경 거절은 통화를 죽이지 않습니다.
call_not_active·room_unknown·hangup_failed·options_failed어느 쪽이든 통화는 살아 있으니, 사유를 받고 대화를 이어가세요. 202는 접수이지 적용이 아닙니다 — 통화 옵션은 Get Call의options_pending으로 반영을 확인하세요.- 통화가 어떻게 끝났는지는 오류가 아니라 통화 객체의 값입니다 —
Get Call의
end_reason을 보세요.
엔드포인트별로 어떤 코드가 나는지는 적지 않습니다 — 코드가 늘 때마다 두 곳을 고쳐야 하고, 실제로 한쪽이 부분집합인 채로 남은 적이 있습니다.