오류 코드
오류는 적절한 HTTP 상태와 { ok: false, error: { code, message, field?, meta?, request_id } } 형식을 반환합니다. 200은 사용하지 않으며 /v1 및 임베드 코드는 백엔드 레지스트리와 동기화됩니다.
오류 응답 구조
http
HTTP/1.1 402 Payment Required
Content-Type: application/json
X-Request-Id: 018f2b00-0000-7000-8000-0000000000ff
{
"ok": false,
"error": {
"code": "wallet.insufficient_balance",
"message": "Số dư ví không đủ để thực hiện giao dịch.",
"field": null,
"meta": null,
"request_id": "018f2b00-0000-7000-8000-0000000000ff"
}
}0000 대신 domain.reason을 쓰는 이유
HTTP 표준: 프록시, 모니터링 및 REST 클라이언트가 이미 HTTP 상태를 이해합니다.
명확한 의미: wallet.insufficient_balance는 표 없이도 이해할 수 있고 새 코드가 기존 로직을 깨지 않습니다.
기존 시스템이 숫자를 요구하나요? 불변 코드 문자열을 번역 키로 사용하세요.
오류 처리 권장 사항 4가지
HTTP 상태 우선, 오류 코드 다음
상태 코드는 재시도·알림·차단을, error.code는 사용자 메시지를 결정합니다. 상태 코드가 더 안정적입니다.
알 수 없는 코드로 흐름을 중단하지 않기
알 수 없는 코드는 HTTP 상태로 처리하고 코드와 request_id를 기록한 후 기본 메시지를 표시하세요. 처리되지 않은 예외를 발생시키지 마세요.
항상 request_id 기록
모든 응답과 오류에 X-Request-Id가 있습니다. 기술 지원 조사에 필요한 요청 식별자입니다.
429 및 503의 Retry-After 준수
고정 대기 시간 대신 Retry-After를 읽으세요. 429에는 X-RateLimit-Limit / Remaining / Reset도 포함됩니다.
코드 ↔ HTTP 대응표
도메인별로 구분합니다. 각 엔드포인트의 오류 분기는 API 레퍼런스에서 확인하세요.
91개 중 91개 코드 표시
인증, 키 및 한도 partner.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
partner.invalid_key | 401 | API 키가 올바르지 않습니다. | 아니요: 같은 요청은 다시 실패 |
partner.invalid_signature | 401 | 요청 서명이 올바르지 않습니다. | 아니요: 같은 요청은 다시 실패 |
partner.timestamp_expired | 401 | 요청 타임스탬프와 서버 시간의 차이가 너무 큽니다. | 아니요: 같은 요청은 다시 실패 |
partner.nonce_reused | 401 | 이미 사용한 nonce입니다. | 아니요: 같은 요청은 다시 실패 |
partner.ip_not_allowed | 403 | 허용 목록에 없는 IP 주소입니다. | 아니요: 같은 요청은 다시 실패 |
partner.scope_denied | 403 | 이 API 키에는 해당 엔드포인트를 호출할 권한이 없습니다. | 아니요: 같은 요청은 다시 실패 |
partner.key_suspended | 403 | 일시 정지된 API 키입니다. | 아니요: 같은 요청은 다시 실패 |
partner.key_revoked | 403 | 폐기된 API 키입니다. | 아니요: 같은 요청은 다시 실패 |
partner.endpoint_disabled | 503 | 엔드포인트 그룹을 일시적으로 사용할 수 없습니다. | 예: 동일 요청 재시도 |
partner.rate_limited | 429 | 호출 한도를 초과했습니다. 나중에 다시 시도하세요. | 예: 동일 요청 재시도 |
partner.package_not_visible | 404 | 이 키로 구매할 수 없는 패키지 코드입니다. | 아니요: 같은 요청은 다시 실패 |
partner.daily_quota_exceeded | 429 | 일일 호출 한도를 초과했습니다. 내일 다시 시도하세요. | 예: 동일 요청 재시도 |
partner.wrong_host | 403 | 이 호스트에서는 해당 키를 사용할 수 없습니다. `tke_test_`는 `https://api-testing.tongkhoesim.com`, `tke_live_`는 `https://api.tongkhoesim.com`을 호출하세요. | 아니요: 같은 요청은 다시 실패 |
일반 API 오류 internal.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
internal.validation_failed | 400 | 제출한 데이터가 올바르지 않습니다. | 아니요: 같은 요청은 다시 실패 |
internal.invalid_filter | 400 | 필터 조건이 올바르지 않습니다. | 아니요: 같은 요청은 다시 실패 |
internal.invalid_sort_field | 400 | 정렬 필드가 올바르지 않습니다. | 아니요: 같은 요청은 다시 실패 |
internal.unauthenticated | 401 | 로그인하지 않았거나 세션이 만료되었습니다. | 아니요: 같은 요청은 다시 실패 |
internal.forbidden | 403 | 이 작업을 수행할 권한이 없습니다. | 아니요: 같은 요청은 다시 실패 |
internal.not_found | 404 | 요청한 리소스를 찾을 수 없습니다. | 아니요: 같은 요청은 다시 실패 |
internal.route_not_found | 404 | 경로가 없습니다. 문서에서 메서드와 경로를 확인하세요. | 아니요: 같은 요청은 다시 실패 |
internal.method_not_allowed | 405 | 지원하지 않는 HTTP 메서드입니다. | 아니요: 같은 요청은 다시 실패 |
internal.conflict | 409 | 상태 충돌로 작업을 완료할 수 없습니다. | 아니요: 같은 요청은 다시 실패 |
internal.unsupported_media_type | 415 | 지원하지 않는 콘텐츠 유형입니다. | 아니요: 같은 요청은 다시 실패 |
internal.rate_limited | 429 | 요청이 너무 많습니다. 나중에 다시 시도하세요. | 예: 동일 요청 재시도 |
internal.server_error | 500 | 시스템 오류가 발생했습니다. 나중에 다시 시도하세요. | 예: 동일 요청 재시도 |
internal.service_unavailable | 503 | 서비스를 일시적으로 사용할 수 없습니다. 나중에 다시 시도하세요. | 예: 동일 요청 재시도 |
internal.limit_exceeded | 422 | 허용 한도에 도달했습니다. 일부 항목을 삭제한 후 다시 시도하세요. | 아니요: 같은 요청은 다시 실패 |
internal.money_frozen | 503 | 원장 확인을 위해 금융 거래가 일시 중지되었습니다. 나중에 다시 시도하세요. | 예: 동일 요청 재시도 |
요금제 목록 catalog.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
catalog.cursor_mismatch | 400 | 페이지 커서가 현재 필터와 맞지 않습니다. cursor 매개변수를 제외하고 첫 페이지부터 다시 조회하세요. | 아니요: 같은 요청은 다시 실패 |
견적 및 주문 order.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
order.quote_not_found | 404 | 견적을 찾을 수 없습니다. | 아니요: 같은 요청은 다시 실패 |
order.quote_expired | 409 | 견적이 만료되었습니다. 새 견적을 만드세요. | 아니요: 같은 요청은 다시 실패 |
order.quote_already_committed | 409 | 이미 주문으로 확정된 견적입니다. | 아니요: 같은 요청은 다시 실패 |
order.package_not_allowed | 400 | 패키지가 없거나 구매할 수 없습니다. | 아니요: 같은 요청은 다시 실패 |
order.not_deliverable | 409 | 주문이 완료되지 않아 이 작업을 수행할 수 없습니다. | 아니요: 같은 요청은 다시 실패 |
order.quantity_limit_exceeded | 400 | eSIM 수가 주문당 허용 한도를 초과합니다. | 아니요: 같은 요청은 다시 실패 |
order.fulfillment_config_changed | 409 | 패키지 제공 설정이 변경되었습니다. 새 견적을 만드세요. | 아니요: 같은 요청은 다시 실패 |
order.refund_suggestion_pending | 409 | 승인 대기 중인 환불 제안이 있습니다. 먼저 처리하세요. | 아니요: 같은 요청은 다시 실패 |
eSIM esim.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
esim.out_of_stock | 409 | 이 패키지의 재고가 부족합니다. | 아니요: 같은 요청은 다시 실패 |
esim.not_cancellable | 409 | 이 eSIM은 취소할 수 있는 상태가 아닙니다. | 아니요: 같은 요청은 다시 실패 |
esim.lpa_not_available | 409 | 이 eSIM에는 아직 LPA 코드가 없습니다(미활성화 또는 미등록). | 아니요: 같은 요청은 다시 실패 |
esim.not_eligible | 409 | 이 eSIM은 충전 조건을 충족하지 않습니다. | 아니요: 같은 요청은 다시 실패 |
esim.not_activated | 409 | 아직 설치되지 않은 eSIM입니다. 설치하고 활성화한 후 충전하세요. | 아니요: 같은 요청은 다시 실패 |
esim.topup_not_supported | 409 | 충전을 지원하지 않는 패키지입니다. | 아니요: 같은 요청은 다시 실패 |
esim.topup_provider_mismatch | 409 | 충전 패키지와 eSIM의 공급업체가 다릅니다. 해당 eSIM용 충전 패키지를 선택하세요. | 아니요: 같은 요청은 다시 실패 |
esim.refund_not_eligible | 409 | 이 eSIM은 환불 요청 조건을 충족하지 않습니다. | 아니요: 같은 요청은 다시 실패 |
esim.refund_request_pending | 409 | 이 eSIM의 환불 요청이 이미 대기 중입니다. | 아니요: 같은 요청은 다시 실패 |
esim.suspended | 409 | 정지된 eSIM입니다. 이 작업을 수행하기 전에 재개하세요. | 아니요: 같은 요청은 다시 실패 |
지갑 wallet.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
wallet.frozen | 409 | 동결된 지갑이므로 결제할 수 없습니다. | 아니요: 같은 요청은 다시 실패 |
wallet.velocity_hold | 409 | 지갑이 검토를 위해 보류되었습니다. 지원팀에 문의하세요. | 아니요: 같은 요청은 다시 실패 |
wallet.insufficient_balance | 402 | 거래를 완료하기에 지갑 잔액이 부족합니다. | 아니요: 같은 요청은 다시 실패 |
wallet.deleted | 409 | 대상 지갑이 삭제되어 유효하지 않습니다. | 아니요: 같은 요청은 다시 실패 |
wallet.velocity_exceeded | 429 | 단시간에 거래 한도를 초과하여 지갑이 검토 대기 상태로 전환되었습니다. | 아니요: 같은 요청은 다시 실패 |
환불 요청 refund.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
refund.note_required | 400 | 환불 거절 사유를 입력하세요. | 아니요: 같은 요청은 다시 실패 |
판매가 pricing.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
pricing.no_price_for_buyer | 409 | 이 패키지에 적용할 판매 가격이 아직 설정되지 않았습니다. | 아니요: 같은 요청은 다시 실패 |
eSIM 공급사 provider.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
provider.disabled | 409 | 패키지 공급업체가 비활성화되어 판매할 수 없습니다. | 아니요: 같은 요청은 다시 실패 |
provider.api_group_disabled | 409 | 공급업체의 서비스 그룹이 비활성화되었습니다. | 아니요: 같은 요청은 다시 실패 |
provider.not_registered | 500 | 이 공급업체의 연결 설정이 없습니다. | 예: 동일 요청 재시도 |
provider.unsupported | 400 | 공급업체가 이 작업을 지원하지 않습니다. | 아니요: 같은 요청은 다시 실패 |
provider.timeout | 504 | 공급업체 응답 시간이 초과되었습니다. 나중에 다시 시도하세요. | 예: 동일 요청 재시도 |
provider.auth_failed | 502 | 공급업체 인증에 실패했습니다. | 예: 동일 요청 재시도 |
provider.rate_limited | 429 | 공급업체가 요청 횟수를 제한하고 있습니다. 나중에 다시 시도하세요. | 예: 동일 요청 재시도 |
provider.malformed_response | 502 | 공급업체가 올바르지 않은 데이터를 반환했습니다. | 예: 동일 요청 재시도 |
provider.call_failed | 502 | 공급업체 호출에 실패했습니다. | 예: 동일 요청 재시도 |
provider.out_of_stock | 409 | 공급업체의 패키지 재고가 없습니다. | 아니요: 같은 요청은 다시 실패 |
provider.msisdn_taken | 409 | 이 MSISDN은 공급업체의 다른 가입자에게 이미 속해 있습니다. | 아니요: 같은 요청은 다시 실패 |
provider.msisdn_exhausted | 409 | 공급업체에 사용 가능한 MSISDN이 없습니다. | 아니요: 같은 요청은 다시 실패 |
Idempotency idempotency.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
idempotency.conflict | 409 | 이 Idempotency-Key는 다른 내용에 이미 사용되었습니다. | 아니요: 같은 요청은 다시 실패 |
공급사 기능 capability.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
capability.not_supported | 422 | 이 패키지는 요청한 기능을 지원하지 않습니다. | 아니요: 같은 요청은 다시 실패 |
테스트 모드 / 시뮬레이션 testmode.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
testmode.live_key_forbidden | 403 | 이 엔드포인트는 api-testing에서 테스트 키(`tke_test_`)로만 사용합니다. 같은 조직의 테스트 키로 바꾸세요. | 아니요: 같은 요청은 다시 실패 |
testmode.not_test_item | 422 | 실제 eSIM이므로 테스트 모드에서 변경할 수 없습니다. 실제 주문을 취소하려면 환불 검토를 요청하세요. | 아니요: 같은 요청은 다시 실패 |
testmode.invalid_scenario | 422 | 잘못된 시나리오입니다. `data_used_mb`, `status`, `expire_now`, `throttled` 중 하나 이상을 보내세요. `status`는 released|installed|enabled|disabled만 허용합니다. | 아니요: 같은 요청은 다시 실패 |
testmode.no_sellable_package | 422 | 샘플 데이터를 만들려면 가격이 설정된 판매 가능 패키지가 하나 이상 필요합니다. | 아니요: 같은 요청은 다시 실패 |
Webhook webhook.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
webhook.health_check_failed | 422 | 상태 확인에서 웹훅 URL이 올바르게 응답하지 않았습니다. | 아니요: 같은 요청은 다시 실패 |
webhook.url_not_allowed | 422 | 허용되지 않는 웹훅 URL입니다. 공개 https URL이어야 합니다. | 아니요: 같은 요청은 다시 실패 |
webhook.not_configured | 404 | 이 API 키에 설정된 웹훅이 없습니다. | 아니요: 같은 요청은 다시 실패 |
webhook.delivery_not_found | 404 | 웹훅 전송을 찾을 수 없습니다. | 아니요: 같은 요청은 다시 실패 |
webhook.already_configured | 409 | 이 API 키에는 이미 웹훅이 있습니다(키당 하나). `PUT /v1/webhooks/{id}`로 수정하거나 `DELETE /v1/webhooks/{id}`로 삭제한 후 새로 만드세요. | 아니요: 같은 요청은 다시 실패 |
webhook.not_found | 404 | 호출한 API 키에 해당 웹훅 엔드포인트가 없습니다. | 아니요: 같은 요청은 다시 실패 |
조직 상태 org.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
org.invoice_profile_incomplete | 422 | 조직의 청구 정보가 불완전합니다(법인명, 세금 번호, 주소). | 아니요: 같은 요청은 다시 실패 |
org.suspended | 403 | 현재 정지된 조직입니다. | 아니요: 같은 요청은 다시 실패 |
org.closed | 403 | 폐쇄된 조직입니다. | 아니요: 같은 요청은 다시 실패 |
공개 임베드 embed.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
embed.invalid_token | 401 | 임베드 토큰이 올바르지 않습니다. | 아니요: 같은 요청은 다시 실패 |
embed.token_revoked | 410 | 더 이상 활성 상태가 아닌 임베드 채널입니다. | 아니요: 같은 요청은 다시 실패 |
embed.rate_limited | 429 | 임베드 채널의 호출이 너무 빠릅니다. 나중에 다시 시도하세요. | 예: 동일 요청 재시도 |
embed.package_not_visible | 404 | 패키지를 찾을 수 없습니다. | 아니요: 같은 요청은 다시 실패 |
VAT 청구서 invoice.*
| code | HTTP | 의미 | 재시도 가능? |
|---|---|---|---|
invoice.order_not_settled | 409 | 환불이 처리 중이므로 주문이 정산되지 않았습니다. 몇 분 후 청구서 발행을 다시 시도하세요. | 예: 동일 요청 재시도 |
invoice.profile_incomplete | 422 | 조직의 청구 정보가 불완전합니다. 청구서를 발행하기 전에 모두 입력하세요. | 아니요: 같은 요청은 다시 실패 |
invoice.already_requested | 409 | 이 주문에는 이미 청구서가 있습니다. | 아니요: 같은 요청은 다시 실패 |
invoice.nothing_to_invoice | 409 | 환불액을 차감하면 청구할 금액이 없습니다. | 아니요: 같은 요청은 다시 실패 |
invoice.amount_changed | 409 | 새 환불로 주문 금액이 변경되었습니다. 금액을 확인하고 청구서를 다시 발행하세요. | 예: 동일 요청 재시도 |
invoice.buyer_invalid | 422 | 청구서 수신 회사 정보가 불완전하거나 세금 번호 형식이 잘못되었습니다. | 아니요: 같은 요청은 다시 실패 |