# 통화 목록 조회 (/docs/api/calls/listCalls)
**GET /v1/accounts/{accountId}/calls**
```json
{
  "operationId": "listCalls",
  "summary": "통화 목록 조회",
  "description": "계정의 통화 로그를 페이지네이션으로 조회합니다.",
  "tags": [
    "Calls"
  ],
  "parameters": [
    {
      "name": "accountId",
      "in": "path",
      "required": true,
      "schema": {
        "type": "string"
      },
      "description": "계정 ID",
      "example": "AC1a2b3c4d"
    },
    {
      "name": "status",
      "in": "query",
      "schema": {
        "type": "string",
        "enum": [
          "queued",
          "ringing",
          "in-progress",
          "completed",
          "busy",
          "no-answer",
          "failed",
          "canceled",
          "rejected"
        ]
      },
      "description": "통화 상태로 필터링. 실패 사유를 더 세분해 조회하려면 `hangupCause` 를 사용하세요.\n",
      "example": "completed"
    },
    {
      "name": "from",
      "in": "query",
      "style": "form",
      "explode": true,
      "schema": {
        "oneOf": [
          {
            "type": "string",
            "example": "07052358010"
          },
          {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "07052358010",
              "07012345678"
            ]
          }
        ]
      },
      "description": "발신번호로 필터링. 단일 값 또는 배열 (?from=070...&from=070...)",
      "example": "07052358010"
    },
    {
      "name": "to",
      "in": "query",
      "style": "form",
      "explode": true,
      "schema": {
        "oneOf": [
          {
            "type": "string",
            "example": "01012345678"
          },
          {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "01012345678",
              "07012345678"
            ]
          }
        ]
      },
      "description": "수신번호로 필터링. 단일 값 또는 배열 (?to=010...&to=010...)",
      "example": "01012345678"
    },
    {
      "name": "number",
      "in": "query",
      "style": "form",
      "explode": true,
      "schema": {
        "oneOf": [
          {
            "type": "string",
            "example": "07052358010"
          },
          {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "07052358010",
              "07012345678"
            ]
          }
        ]
      },
      "description": "관여 번호로 필터링 (from OR to 매칭). 해당 번호가 발신 또는 수신에 포함된 통화를 조회합니다. 단일 값 또는 배열",
      "example": "07012345678"
    },
    {
      "name": "since",
      "in": "query",
      "allowReserved": true,
      "schema": {
        "type": "string",
        "format": "date-time"
      },
      "description": "이 시각 **이후**(포함) 시작된 통화만. ISO 8601.",
      "example": "2026-08-01T00:00:00Z"
    },
    {
      "name": "until",
      "in": "query",
      "allowReserved": true,
      "schema": {
        "type": "string",
        "format": "date-time"
      },
      "description": "이 시각 **이전**(미포함) 시작된 통화만. ISO 8601. `since` 와 함께 쓰면 구간이 겹치지 않게 이어붙일 수 있습니다.\n",
      "example": "2026-09-01T00:00:00Z"
    },
    {
      "name": "minDuration",
      "in": "query",
      "schema": {
        "type": "integer",
        "minimum": 0,
        "maximum": 2147483647
      },
      "description": "통화 시간(초) 하한. 이 값 **포함**.",
      "example": 10
    },
    {
      "name": "maxDuration",
      "in": "query",
      "schema": {
        "type": "integer",
        "minimum": 0,
        "maximum": 2147483647
      },
      "description": "통화 시간(초) 상한. 이 값 **포함**. `minDuration=0&maxDuration=0` 이면 0초 통화만.",
      "example": 60
    },
    {
      "name": "hangupCause",
      "in": "query",
      "style": "form",
      "explode": true,
      "schema": {
        "oneOf": [
          {
            "type": "string",
            "example": "concurrency_limit_exceeded"
          },
          {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "concurrency_limit_exceeded",
              "invalid_number"
            ]
          }
        ]
      },
      "description": "종료 사유로 필터링. `status` 가 실패를 한 덩어리로 묶는 것과 달리 **왜** 끝났는지로 가릅니다 — 채널 초과(`concurrency_limit_exceeded`)·결번(`invalid_number`)· 한국AI통신 오류(`app_error`) 등. 종료 사유 필터는 [Calls API](/api-reference/claw-ops-api/calls/list-calls)를 참고하세요. 단일 값 또는 배열.\n",
      "example": "concurrency_limit_exceeded"
    },
    {
      "name": "pageSize",
      "in": "query",
      "schema": {
        "type": "integer",
        "default": 20,
        "maximum": 100
      },
      "description": "페이지당 항목 수 (최대 100)",
      "example": 20
    },
    {
      "name": "page_size",
      "in": "query",
      "deprecated": true,
      "schema": {
        "type": "integer",
        "maximum": 100
      },
      "description": "`pageSize` 의 별칭(하위호환). 신규 연동은 `pageSize` 를 사용하세요.",
      "example": 50
    },
    {
      "name": "limit",
      "in": "query",
      "deprecated": true,
      "schema": {
        "type": "integer",
        "maximum": 100
      },
      "description": "`pageSize` 의 별칭(하위호환). 신규 연동은 `pageSize` 를 사용하세요.",
      "example": 100
    },
    {
      "name": "page",
      "in": "query",
      "schema": {
        "type": "integer",
        "default": 0
      },
      "description": "페이지 번호 (0부터 시작)",
      "example": 0
    }
  ],
  "responses": {
    "200": {
      "description": "통화 목록",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "callId": {
                      "type": "string",
                      "example": "CAabcdef1234567890"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "queued",
                        "ringing",
                        "in-progress",
                        "completed",
                        "failed",
                        "busy",
                        "no-answer",
                        "canceled",
                        "rejected"
                      ],
                      "description": "통화 상태. 진행 중: queued(발신 대기) / ringing(벨) / in-progress(통화 중).\n종료 상태(실제 종료 사유): completed(응답 후 정상 종료) / no-answer(벨은 울렸으나 무응답,\nTimeout 초과로 발신 취소) / busy(통화중) / rejected(수신 거절) / canceled(응답 전 발신 측 취소) /\nfailed(시스템·망 오류). completed 만이 통화가 실제로 연결됐음을 의미합니다.\n"
                    },
                    "to": {
                      "type": "string",
                      "example": "01012345678"
                    },
                    "from": {
                      "type": "string",
                      "example": "07052358010"
                    },
                    "direction": {
                      "type": "string",
                      "enum": [
                        "outbound",
                        "inbound"
                      ]
                    },
                    "duration": {
                      "type": "integer",
                      "nullable": true,
                      "description": "통화 시간 (초)",
                      "example": 30
                    },
                    "accountId": {
                      "type": "string",
                      "example": "AC1a2b3c4d"
                    },
                    "answeredBy": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "human",
                        "machine",
                        "unknown",
                        null
                      ],
                      "description": "AMD(MachineDetection) 결과. MachineDetection 을 켠 발신 통화에만 값이 있으며,\n사람 응답=human, 자동응답기/음성사서함=machine, 판정 불가=unknown. 미사용 시 null.\n"
                    },
                    "forwardedFrom": {
                      "type": "string",
                      "nullable": true,
                      "description": "착신전환으로 들어온 수신 통화에서 발신자가 원래 건 번호. `to` 는 실제로 받은 번호입니다.\n발신 통화와 직접 걸려온 수신 통화, 이 필드가 생기기 전의 수신 통화는 null.\n"
                    },
                    "callerIdRestricted": {
                      "type": "boolean",
                      "nullable": true,
                      "description": "수신 통화에서 발신자가 번호 표시제한(`*23#` 등)을 걸고 건 통화면 true, 아니면 false.\n번호는 `from` 에 그대로 전달됩니다. 통신사가 표시제한 여부를 보내지 않는 경로로 들어온\n통화는 false 로 올 수 있습니다. 발신 통화와 2026-09-29 이전의 수신 통화는 null.\n",
                      "example": false
                    },
                    "recordingUrl": {
                      "type": "string",
                      "nullable": true,
                      "description": "녹음 다운로드 경로 (녹음이 있는 통화만)",
                      "example": "/v1/accounts/AC1a2b3c4d/recordings/CAabcdef1234567890"
                    },
                    "hangupCause": {
                      "type": "string",
                      "nullable": true,
                      "description": "통화 종료 사유. `status` 가 왜 그렇게 끝났는지를 구분합니다. 종료 전이거나 사유\n미상이면 null.\n\n네 갈래로 나뉩니다 — 번호 자체가 문제라 재시도해도 소용없는 것(`invalid_number` 등),\n일시적이라 재시도 가치가 있는 것(`no_answer`·`user_busy` 등), 번호가 아니라 **계정**\n문제인 것(`concurrency_limit_exceeded`·`subscription_inactive` 등), 통화 처리 오류\n(`app_error`·`call_stuck`). `app_error` 에는 웹훅·VoiceML 응답 실패가 포함됩니다.\n\n고객 설정이 받지 않고 거절한 수신 통화는 따로 갈립니다 — 수신 통화 차단 목록에 있는 번호\n(`caller_blocked`), 번호 표시제한 통화 거절 설정(`caller_id_restricted_rejected`). 둘 다\n`status=rejected` 이며 재시도 대상이 아닙니다.\n\n**종료 사유로 통화를 필터링하려면 [Calls API](/api-reference/claw-ops-api/calls/list-calls)를 참고하세요.**\n여기 값을 나열하지 않는 이유: 사유가 늘 때마다 spec·문서·대시보드가 각각 갱신돼야 하는데\n한 곳만 빠지면 조용히 어긋난다(실제로 그렇게 어긋난 적이 있다).\n",
                      "example": "invalid_number"
                    },
                    "hangupCauseQ850": {
                      "type": "integer",
                      "nullable": true,
                      "description": "통신망 Q.850 cause code. 1·5·28=결번, 16=정상해제, 17=통화중, 18/19/20=무응답,\n21=거절, 38=망장애. 사유 미상이면 null.\n",
                      "example": 1
                    },
                    "sipResponseCode": {
                      "type": "integer",
                      "nullable": true,
                      "description": "종료를 유발한 SIP 응답코드 (404=없는 번호, 486=통화중, 500=망 오류 등). 수신 통화를 받지 않고 거절했으면 한국AI통신이 보낸 응답(403=거절, 486=통화중)입니다. 응답코드 없이 끝났으면 null.",
                      "example": 404
                    },
                    "hangupSource": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "carrier",
                        "callee",
                        "caller",
                        "app",
                        "system",
                        null
                      ],
                      "description": "종료 책임 주체. carrier(통신망) / callee(수신 측) / caller(발신 측) /\napp·system(한국AI통신 측 — 이 경우 재시도를 권장합니다). 통화 후 정상 종료에서는\n먼저 끊은 쪽의 역할입니다: 착신 통화에서 상대가 끊으면 caller, 여러분의 앱이\n끊으면 callee(발신 통화는 그 반대). 판정하지 못하면 null.\n",
                      "example": "carrier"
                    },
                    "transferTo": {
                      "type": "string",
                      "nullable": true,
                      "description": "전환 대상 번호. **읽기 전용** — 값을 지정하는 요청 필드는 없고 실제로 일어난 전환의\n결과가 기록됩니다. 전환이 없었으면 null.\n\n에이전트 전환과 SIP REFER 전환에서만 채워집니다(VoiceML `<Dial>` 은 제외 — 그 결과는\n`DialCallStatus` 로 받습니다).\n\n**전체 설명은 [통화 전환 결과](/call-transfers) 가 정본입니다.**\n",
                      "example": "01012345678"
                    },
                    "transferStatus": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "completed",
                        "failed",
                        "no-answer",
                        "busy",
                        "canceled",
                        null
                      ],
                      "description": "전환 결과(읽기 전용, 전환이 없었으면 null).\n\ncompleted(연결 후 정상 종료) / no-answer(대상이 받지 않음) / busy(통화중) /\ncanceled(연결 전에 취소·중단) / failed(그 외 실패 — 사유는 transferHangupCauseQ850 ·\ntransferSipResponseCode · transferReasonText).\n\n**종료된 전환의 결과만 담습니다.** 진행 중 상태는 transfers[].status 또는 Status Callback\n의 `transfer` 이벤트로 보세요.\n"
                    },
                    "transferDuration": {
                      "type": "integer",
                      "nullable": true,
                      "description": "전환 통화 시간(초) — 대상이 받은 시점부터 그 전환 leg 가 끝날 때까지의 **관측값**입니다.\n연결되지 못한 전환은 null.\n\n⚠️ 과금 초가 아닙니다. 과금 대상 초는 transfers[].billableDuration 입니다.\n",
                      "example": 60
                    },
                    "transferHangupCauseQ850": {
                      "type": "integer",
                      "nullable": true,
                      "description": "전환 leg 의 통신망 Q.850 cause (예: 16=정상해제, 17=통화중, 19=무응답, 21=거절,\n38=망장애). 통신망이 SIP 5xx 로 보낸 실패의 실제 사유로 교정된 값. 사유 미상이면 null.\n",
                      "example": 16
                    },
                    "transferSipResponseCode": {
                      "type": "integer",
                      "nullable": true,
                      "description": "전환 leg 의 실제 SIP 응답코드 (예 500). 사유 미상이면 null.",
                      "example": 500
                    },
                    "transferReasonText": {
                      "type": "string",
                      "nullable": true,
                      "description": "전환 실패 원본 진단 텍스트. 사유 미상이면 null.",
                      "example": "CS_NORMAL_RELEASE"
                    },
                    "transfers": {
                      "type": "array",
                      "description": "전환 leg 정본 체인(sequence 순). 다단계/재시도 전환의 전체 이력을 제공한다. **단건 조회에만\n포함되며(리스트 응답엔 생략)**, 전환이 없었던 통화는 빈 배열.\n\n위 transfer* 단일 필드는 이 체인에서 파생한 하위호환용 **대표 leg** 다 — 종료된 leg 중\ncompleted 우선, 동률이면 sequence 최대. 전체 시도는 이 배열로 본다.\n",
                      "items": {
                        "type": "object",
                        "properties": {
                          "sequence": {
                            "type": "integer",
                            "description": "통화 내 전환 순번(1부터)"
                          },
                          "to": {
                            "type": "string",
                            "nullable": true,
                            "description": "전환 대상 번호 또는 SIP URI"
                          },
                          "mode": {
                            "type": "string",
                            "nullable": true,
                            "description": "blind | warm | refer | refer-attended"
                          },
                          "destinationType": {
                            "type": "string",
                            "nullable": true,
                            "description": "pstn(통신망 번호) | sip(SIP URI 직결). 2026-07-10 이전 전환은 null"
                          },
                          "status": {
                            "type": "string",
                            "nullable": true,
                            "description": "initiated|ringing|connected|completed|failed|no-answer|busy|canceled|interrupted"
                          },
                          "duration": {
                            "type": "integer",
                            "nullable": true,
                            "description": "connected→종료 초(관측용)"
                          },
                          "billable": {
                            "type": "boolean",
                            "description": "과금 대상 여부(통화당 대표 leg 1건만 true)"
                          },
                          "billableDuration": {
                            "type": "integer",
                            "nullable": true,
                            "description": "과금 대상 초(completed 대표 leg 만)"
                          },
                          "hangupCauseQ850": {
                            "type": "integer",
                            "nullable": true,
                            "description": "이 leg 의 통신망 Q.850 cause. 사유 미상이면 null"
                          },
                          "sipResponseCode": {
                            "type": "integer",
                            "nullable": true,
                            "description": "이 leg 의 SIP 응답코드. 사유 미상이면 null"
                          },
                          "reasonText": {
                            "type": "string",
                            "nullable": true,
                            "description": "통신망 원본 진단 텍스트(자유 형식 — 분기 조건으로 쓰지 말 것)"
                          },
                          "startedAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true,
                            "description": "전환을 시작한 시각"
                          },
                          "answeredAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true,
                            "description": "대상이 전화를 받은 시각. 응답까지 걸린 시간은 `answeredAt - startedAt` 이다. `connectedAt` 과 다르다 — 연결 전 안내(warm 전환의 whisper)가 있으면 그 안내가 끝난 뒤에 연결되고, 안내 도중 통화가 끝나면 `connectedAt` 은 null 이어도 이 값은 남는다. 받았는지 판정: 이 값이 있으면 받은 것이다 — status 가 failed·canceled·no-answer 여도 그렇다(연결 전 안내 도중 끝난 warm 전환). 이 값이 없으면 connectedAt 이 있거나 status 가 completed 일 때만 받은 것이다. ⚠️ 2026-10-13 이전 전환은 받았어도 null 이고, 안내 도중 끝난 경우는 구분되지 않는다.\n"
                          },
                          "connectedAt": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true,
                            "description": "발신자와 대상이 연결된 시각. 연결 전 안내(whisper)가 있으면 안내가 끝난 뒤 시각이라 `answeredAt` 보다 늦다. 연결되지 못했으면 null. ⚠️ 2026-07-10 이전 전환은 연결됐어도 null 이므로 연결 여부 판정에 쓰지 말 것(status·duration 을 볼 것).\n"
                          },
                          "endedAt": {
                            "type": "string",
                            "format": "date-time",
                            "description": "이 leg 가 끝난 시각",
                            "nullable": true
                          },
                          "timeout": {
                            "type": "integer",
                            "nullable": true,
                            "description": "이 전환에 적용한 대기 한도(초). 대상이 이 시간 안에 받지 않으면 `no-answer` 로 끝난다. 2026-10-13 이전 전환은 null.\n"
                          }
                        }
                      }
                    },
                    "dateCreated": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "dateUpdated": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "통화 종료 시각"
                    }
                  }
                }
              },
              "meta": {
                "type": "object",
                "properties": {
                  "page": {
                    "type": "integer",
                    "description": "현재 페이지"
                  },
                  "pageSize": {
                    "type": "integer",
                    "description": "페이지 크기"
                  },
                  "total": {
                    "type": "integer",
                    "description": "전체 건수"
                  }
                }
              }
            }
          }
        }
      }
    },
    "400": {
      "description": "pageSize 또는 page가 정수가 아닌 경우",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "사람이 읽을 수 있는 에러 메시지"
              },
              "code": {
                "type": "string",
                "description": "기계 판독용 에러 코드. **분기는 이 값으로 하세요** — `error` 문구는 안내를 다듬으면서\n바뀔 수 있지만 코드는 계약입니다.\n\n같은 상태 코드라도 대응이 갈리는 경우가 있습니다. 예를 들어 발신의 `422` 는\n  - `recipient_blocked`: 수신거부 명단에 있는 번호입니다. 재시도하지 말고 명단에서\n    해제해야 발신됩니다.\n  - `quota_exceeded`: 플랜 한도를 넘었습니다. 다음 주기에 다시 발신됩니다.\n\n전화번호 정규화 관련:\n  - `INVALID_PHONE_NUMBER`: 전화번호 포맷 오류 (한국 번호 형식 아님)\n  - `UNSUPPORTED_COUNTRY`: 국제 발신 미지원 (+82 외 국가코드)",
                "example": "recipient_blocked"
              },
              "ip": {
                "type": "string",
                "description": "`overseas_ip_blocked` 일 때만 옵니다. 국외 IP 라고 판정한 요청 IP 입니다.\n해외 리전 클라우드 서버에서 호출한다면 이 값이 그 서버의 공인 IP 이며, 콘솔\n「국외 접속 예외」에서 예외를 신청할 때 이 고정 IP 를 적습니다.",
                "example": "52.79.213.41"
              },
              "processedAt": {
                "type": "string",
                "format": "date-time",
                "description": "문자 발송 요청을 거부한 서버가 오류 응답을 생성한 시각(UTC). 콘솔은 이 값을 한국 시각으로 표시합니다. 인증·요청 형식 검증 등 발송 서비스 진입 전 오류에는 없을 수 있습니다.",
                "example": "2026-10-21T04:10:34.303Z"
              },
              "blockers": {
                "type": "array",
                "description": "`certificate_not_eligible` 일 때만 옵니다. 발급할 수 없는 번호와 그 이유입니다.",
                "items": {
                  "type": "object",
                  "description": "번호 하나가 발급되지 않는(또는 안내가 필요한) 이유.\n\n| code | 뜻 | 발급 |\n|---|---|---|\n| `number_not_owned` | 이 계정이 보유한 번호가 아님(반납·재배정 포함) | 막힘 |\n| `number_not_carrier_issued` | 한국AI통신이 개통한 070·대표번호가 아님(모바일 게이트웨이, 타사 번호) | 막힘 |\n| `business_info_incomplete` | 사업자 계정인데(관리번호 제외) 상호·사업자등록번호·대표자명·사업장 주소 중 빈 칸 | 막힘 |\n| `identity_unverified` | 개인 계정 번호 또는 관리번호인데 그 번호에 본인확인 정보가 없음 | 막힘 |\n| `account_restricted` | 이용 제한 계정 | 막힘 |\n| `business_recheck_pending` | 사업자 계정인데 사업자 정보 재확인이 진행 중(통과 전) — 관리번호를 뺀 계정의 모든 번호 | 막힘 |\n| `holder_is_personal` | 개인 명의로 찍힌다는 안내 | 가능 |",
                  "properties": {
                    "number": {
                      "type": "string",
                      "description": "422 응답의 blockers 에만 있다",
                      "example": "07012340002"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "number_not_owned",
                        "number_not_carrier_issued",
                        "business_info_incomplete",
                        "identity_unverified",
                        "account_restricted",
                        "business_recheck_pending",
                        "holder_is_personal"
                      ],
                      "example": "number_not_carrier_issued"
                    },
                    "message": {
                      "type": "string",
                      "example": "이용증명원은 한국AI통신이 개통한 070·대표번호만 발급됩니다."
                    }
                  },
                  "required": [
                    "code",
                    "message"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "401": {
      "description": "인증 실패",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "사람이 읽을 수 있는 에러 메시지"
              },
              "code": {
                "type": "string",
                "description": "기계 판독용 에러 코드. **분기는 이 값으로 하세요** — `error` 문구는 안내를 다듬으면서\n바뀔 수 있지만 코드는 계약입니다.\n\n같은 상태 코드라도 대응이 갈리는 경우가 있습니다. 예를 들어 발신의 `422` 는\n  - `recipient_blocked`: 수신거부 명단에 있는 번호입니다. 재시도하지 말고 명단에서\n    해제해야 발신됩니다.\n  - `quota_exceeded`: 플랜 한도를 넘었습니다. 다음 주기에 다시 발신됩니다.\n\n전화번호 정규화 관련:\n  - `INVALID_PHONE_NUMBER`: 전화번호 포맷 오류 (한국 번호 형식 아님)\n  - `UNSUPPORTED_COUNTRY`: 국제 발신 미지원 (+82 외 국가코드)",
                "example": "recipient_blocked"
              },
              "ip": {
                "type": "string",
                "description": "`overseas_ip_blocked` 일 때만 옵니다. 국외 IP 라고 판정한 요청 IP 입니다.\n해외 리전 클라우드 서버에서 호출한다면 이 값이 그 서버의 공인 IP 이며, 콘솔\n「국외 접속 예외」에서 예외를 신청할 때 이 고정 IP 를 적습니다.",
                "example": "52.79.213.41"
              },
              "processedAt": {
                "type": "string",
                "format": "date-time",
                "description": "문자 발송 요청을 거부한 서버가 오류 응답을 생성한 시각(UTC). 콘솔은 이 값을 한국 시각으로 표시합니다. 인증·요청 형식 검증 등 발송 서비스 진입 전 오류에는 없을 수 있습니다.",
                "example": "2026-10-21T04:10:34.303Z"
              },
              "blockers": {
                "type": "array",
                "description": "`certificate_not_eligible` 일 때만 옵니다. 발급할 수 없는 번호와 그 이유입니다.",
                "items": {
                  "type": "object",
                  "description": "번호 하나가 발급되지 않는(또는 안내가 필요한) 이유.\n\n| code | 뜻 | 발급 |\n|---|---|---|\n| `number_not_owned` | 이 계정이 보유한 번호가 아님(반납·재배정 포함) | 막힘 |\n| `number_not_carrier_issued` | 한국AI통신이 개통한 070·대표번호가 아님(모바일 게이트웨이, 타사 번호) | 막힘 |\n| `business_info_incomplete` | 사업자 계정인데(관리번호 제외) 상호·사업자등록번호·대표자명·사업장 주소 중 빈 칸 | 막힘 |\n| `identity_unverified` | 개인 계정 번호 또는 관리번호인데 그 번호에 본인확인 정보가 없음 | 막힘 |\n| `account_restricted` | 이용 제한 계정 | 막힘 |\n| `business_recheck_pending` | 사업자 계정인데 사업자 정보 재확인이 진행 중(통과 전) — 관리번호를 뺀 계정의 모든 번호 | 막힘 |\n| `holder_is_personal` | 개인 명의로 찍힌다는 안내 | 가능 |",
                  "properties": {
                    "number": {
                      "type": "string",
                      "description": "422 응답의 blockers 에만 있다",
                      "example": "07012340002"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "number_not_owned",
                        "number_not_carrier_issued",
                        "business_info_incomplete",
                        "identity_unverified",
                        "account_restricted",
                        "business_recheck_pending",
                        "holder_is_personal"
                      ],
                      "example": "number_not_carrier_issued"
                    },
                    "message": {
                      "type": "string",
                      "example": "이용증명원은 한국AI통신이 개통한 070·대표번호만 발급됩니다."
                    }
                  },
                  "required": [
                    "code",
                    "message"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "403": {
      "description": "접근 권한 없음",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "error": {
                "type": "string",
                "description": "사람이 읽을 수 있는 에러 메시지"
              },
              "code": {
                "type": "string",
                "description": "기계 판독용 에러 코드. **분기는 이 값으로 하세요** — `error` 문구는 안내를 다듬으면서\n바뀔 수 있지만 코드는 계약입니다.\n\n같은 상태 코드라도 대응이 갈리는 경우가 있습니다. 예를 들어 발신의 `422` 는\n  - `recipient_blocked`: 수신거부 명단에 있는 번호입니다. 재시도하지 말고 명단에서\n    해제해야 발신됩니다.\n  - `quota_exceeded`: 플랜 한도를 넘었습니다. 다음 주기에 다시 발신됩니다.\n\n전화번호 정규화 관련:\n  - `INVALID_PHONE_NUMBER`: 전화번호 포맷 오류 (한국 번호 형식 아님)\n  - `UNSUPPORTED_COUNTRY`: 국제 발신 미지원 (+82 외 국가코드)",
                "example": "recipient_blocked"
              },
              "ip": {
                "type": "string",
                "description": "`overseas_ip_blocked` 일 때만 옵니다. 국외 IP 라고 판정한 요청 IP 입니다.\n해외 리전 클라우드 서버에서 호출한다면 이 값이 그 서버의 공인 IP 이며, 콘솔\n「국외 접속 예외」에서 예외를 신청할 때 이 고정 IP 를 적습니다.",
                "example": "52.79.213.41"
              },
              "processedAt": {
                "type": "string",
                "format": "date-time",
                "description": "문자 발송 요청을 거부한 서버가 오류 응답을 생성한 시각(UTC). 콘솔은 이 값을 한국 시각으로 표시합니다. 인증·요청 형식 검증 등 발송 서비스 진입 전 오류에는 없을 수 있습니다.",
                "example": "2026-10-21T04:10:34.303Z"
              },
              "blockers": {
                "type": "array",
                "description": "`certificate_not_eligible` 일 때만 옵니다. 발급할 수 없는 번호와 그 이유입니다.",
                "items": {
                  "type": "object",
                  "description": "번호 하나가 발급되지 않는(또는 안내가 필요한) 이유.\n\n| code | 뜻 | 발급 |\n|---|---|---|\n| `number_not_owned` | 이 계정이 보유한 번호가 아님(반납·재배정 포함) | 막힘 |\n| `number_not_carrier_issued` | 한국AI통신이 개통한 070·대표번호가 아님(모바일 게이트웨이, 타사 번호) | 막힘 |\n| `business_info_incomplete` | 사업자 계정인데(관리번호 제외) 상호·사업자등록번호·대표자명·사업장 주소 중 빈 칸 | 막힘 |\n| `identity_unverified` | 개인 계정 번호 또는 관리번호인데 그 번호에 본인확인 정보가 없음 | 막힘 |\n| `account_restricted` | 이용 제한 계정 | 막힘 |\n| `business_recheck_pending` | 사업자 계정인데 사업자 정보 재확인이 진행 중(통과 전) — 관리번호를 뺀 계정의 모든 번호 | 막힘 |\n| `holder_is_personal` | 개인 명의로 찍힌다는 안내 | 가능 |",
                  "properties": {
                    "number": {
                      "type": "string",
                      "description": "422 응답의 blockers 에만 있다",
                      "example": "07012340002"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "number_not_owned",
                        "number_not_carrier_issued",
                        "business_info_incomplete",
                        "identity_unverified",
                        "account_restricted",
                        "business_recheck_pending",
                        "holder_is_personal"
                      ],
                      "example": "number_not_carrier_issued"
                    },
                    "message": {
                      "type": "string",
                      "example": "이용증명원은 한국AI통신이 개통한 070·대표번호만 발급됩니다."
                    }
                  },
                  "required": [
                    "code",
                    "message"
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}
```