> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tryvox.co/llms.txt
> Use this file to discover all available pages before exploring further.

# 통화 생성

> 아웃바운드 통화를 생성합니다. 발신 번호에 활성 traffic split 정책이 없을 때만 `agent`를 직접 지정할 수 있습니다.



## OpenAPI

````yaml /api-reference/v3/openapi.json post /calls
openapi: 3.1.0
info:
  title: vox.ai API
  description: >
    vox.ai API v3


    ### v3 공개 계약 규칙


    - 인증은 `Authorization: Bearer <token>` 헤더를 사용합니다. 조직 API 키를 Bearer 토큰으로
    전달합니다.

    - 요청과 응답 필드는 기본적으로 `snake_case`를 사용합니다.

    - `agent.data`는 에이전트 레지스트리와 호환되어야 하므로 `callSettings`, `toolIds`,
    `builtInTools`, `presetDynamicVariables` 같은 camelCase 필드를 유지합니다.

    - `_at`으로 끝나는 타임스탬프는 unix milliseconds입니다. 일부 입력값은 호환성을 위해 10~11자리 unix
    seconds도 허용하고 milliseconds로 정규화합니다.

    - 캠페인 통화 가능 시간은 분 단위 정수(`start_min` / `end_min`)를 사용합니다. 알림 스케줄은 `HH:MM`
    문자열(`start_time` / `end_time`)을 사용합니다.

    - 응답 객체 자신의 식별자는 `id`입니다. 다른 리소스를 참조하는 필드와 path parameter는 `agent_id`,
    `call_id`, `telephone_line_id`처럼 명시적인 이름을 사용합니다.

    - 실패 응답은 `{ "error": { "code", "message", "details" } }` 형태입니다. 가능한 경우
    `details.field`, `details.reason`, `details.allowed_values`를 함께 제공합니다.
  version: 3.0.0
servers:
  - url: https://client-api.tryvox.co/v3
    description: 운영
security: []
paths:
  /calls:
    post:
      tags:
        - Calls
      summary: 통화 생성
      description: >-
        아웃바운드 통화를 생성합니다. 발신 번호에 활성 traffic split 정책이 없을 때만 `agent`를 직접 지정할 수
        있습니다.
      operationId: createCall
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCallRequest'
            examples:
              explicit_agent:
                summary: agent 명시 + 동적 변수
                value:
                  from_number: '07011112222'
                  to_number: '01012345678'
                  agent:
                    agent_id: 7f3e9c12-4a8b-4d5e-9f1a-2b3c4d5e6f7a
                    agent_version: current
                  dynamic_variables:
                    customer_name: 홍길동
                    appointment_at: 2026-05-10 14:00
                  metadata:
                    crm_lead_id: lead_a1b2c3
              traffic_split_routing:
                summary: agent 생략 — 발신 번호의 traffic split 정책으로 라우팅
                value:
                  from_number: '07011112222'
                  to_number: '01012345678'
                  presentation_number: '07012345678'
        description: >-
          아웃바운드 통화 생성 요청입니다. 에이전트를 직접 지정하려면 `agent`를 보냅니다. 발신 번호에 활성 traffic
          split 정책이 있으면 `agent`를 생략합니다.
      responses:
        '201':
          description: 성공 응답
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallResponse'
        '400':
          description: 요청 검증 오류
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                validationError:
                  summary: 요청 검증 오류
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: Request validation failed.
                      details:
                        field: name
                        reason: must not be blank
        '401':
          description: 인증이 필요합니다.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                unauthorized:
                  summary: Bearer 토큰 누락 또는 오류
                  value:
                    error:
                      code: UNAUTHORIZED
                      message: Authentication is required.
                      details: {}
        '403':
          description: 권한이 없습니다.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                forbidden:
                  summary: 권한이 없습니다.
                  value:
                    error:
                      code: FORBIDDEN
                      message: Permission denied.
                      details: {}
        '404':
          description: 리소스를 찾을 수 없습니다.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                notFound:
                  summary: 리소스를 찾을 수 없습니다.
                  value:
                    error:
                      code: RESOURCE_NOT_FOUND
                      message: Resource not found.
                      details:
                        resource: agent
        '409':
          description: 충돌이 발생했습니다.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                conflict:
                  summary: 상태 충돌이 발생했습니다.
                  value:
                    error:
                      code: CONFLICT
                      message: The requested operation conflicts with current state.
                      details:
                        current_status: draft
        '429':
          description: 요청 한도를 초과했습니다.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                rateLimited:
                  summary: 요청 한도를 초과했습니다.
                  value:
                    error:
                      code: RATE_LIMIT_EXCEEDED
                      message: Too many requests.
                      details:
                        limit: 5
                        window_seconds: 1
        '500':
          description: 서버 내부 오류가 발생했습니다.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                internalError:
                  summary: 서버 내부 오류가 발생했습니다.
                  value:
                    error:
                      code: INTERNAL_ERROR
                      message: Internal server error.
                      details: {}
        '503':
          description: 서비스를 일시적으로 사용할 수 없습니다.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                serviceUnavailable:
                  summary: 서비스를 일시적으로 사용할 수 없습니다.
                  value:
                    error:
                      code: SERVICE_UNAVAILABLE
                      message: Service temporarily unavailable.
                      details: {}
      security:
        - BearerAuth: []
components:
  schemas:
    CreateCallRequest:
      properties:
        from_number:
          type: string
          title: From Number
          description: '발신 번호. 하이픈 없는 국내 번호 (예: ''01012345678''). libphonenumber 검증.'
        to_number:
          type: string
          title: To Number
          description: '수신 번호. 하이픈 없는 국내 번호 (예: ''01098765432'').'
        agent:
          anyOf:
            - $ref: '#/components/schemas/AgentMapping'
            - type: 'null'
          description: '`agent_id`와 `agent_version`으로 구성된 에이전트 매핑 객체입니다.'
        metadata:
          additionalProperties: true
          type: object
          title: Metadata
          description: 자유 형식 메타데이터입니다. 응답에서 그대로 반환합니다.
        dynamic_variables:
          additionalProperties: true
          type: object
          title: Dynamic Variables
          description: 에이전트 프롬프트에 주입할 동적 변수. {{변수명}} 치환.
        presentation_number:
          anyOf:
            - type: string
            - type: 'null'
          title: Presentation Number
          description: 수신자에게 표시할 번호. 조직 승인 번호만 허용. 미지정은 null.
      additionalProperties: false
      type: object
      required:
        - from_number
        - to_number
      title: CreateCallRequest
      description: v3 아웃바운드 통화 생성 요청.
    CallResponse:
      properties:
        id:
          type: string
          title: Id
          description: 통화의 고유 식별자입니다.
        customer_id:
          anyOf:
            - type: string
              format: uuid
            - type: 'null'
          title: Customer Id
          description: 서버가 귀속한 고객 UUID입니다. 아직 고객 귀속이 완료되지 않았거나 귀속 대상이 없으면 null입니다.
        agent:
          $ref: '#/components/schemas/AgentMapping'
          description: '`agent_id`와 `agent_version`으로 구성된 에이전트 매핑 객체입니다.'
        call_type:
          $ref: '#/components/schemas/CallTypeV3Enum'
          description: 통화 유형. 'web'/'phone'/'inbound'/'outbound'/'api' 중 하나.
        from_number:
          anyOf:
            - type: string
            - type: 'null'
          title: From Number
          description: '발신 번호. 하이픈 없는 국내 번호 (예: ''01012345678''). 웹 통화 등은 null.'
        to_number:
          anyOf:
            - type: string
            - type: 'null'
          title: To Number
          description: 수신 번호. 하이픈 없는 국내 번호.
        presentation_number:
          anyOf:
            - type: string
            - type: 'null'
          title: Presentation Number
          description: >-
            수신자에게 표시할 발신 번호. 조직이 사전 등록·승인한 번호
            (`telephone_presentation_numbers`). 미지정 시 null.
        status:
          $ref: '#/components/schemas/CallStatusV3Enum'
          description: 통화 상태입니다. 값 의미와 전이 규칙은 `CallStatusV3Enum` 정의를 참고합니다.
        disconnection_reason:
          anyOf:
            - $ref: '#/components/schemas/DisconnectionReasonDto'
            - type: 'null'
          description: 통화 종료 사유. 진행 중이면 null.
        start_at:
          anyOf:
            - type: integer
            - type: 'null'
          title: Start At
          description: 통화가 시작된 시각 (unix ms). 통화가 아직 시작되지 않았으면 null.
        end_at:
          anyOf:
            - type: integer
            - type: 'null'
          title: End At
          description: 통화가 종료된 시각 (unix ms). 통화가 아직 종료되지 않았으면 null.
        recording_url:
          anyOf:
            - type: string
            - type: 'null'
          title: Recording Url
          description: 녹음 파일 URL. 녹음 없거나 옵트아웃이면 null.
        metadata:
          additionalProperties: true
          type: object
          title: Metadata
          description: 요청 시 전달된 정적 메타데이터를 그대로 반환합니다.
        dynamic_variables:
          additionalProperties: true
          type: object
          title: Dynamic Variables
          description: 요청 시 전달된 동적 변수를 그대로 반환합니다.
        call_analysis:
          anyOf:
            - $ref: '#/components/schemas/CallV3AnalysisDto'
            - type: 'null'
          description: 통화 분석 결과입니다(요약, 감정, 사용자 정의 분석). 분석 완료 전에는 null입니다.
        call_cost:
          anyOf:
            - $ref: '#/components/schemas/CallV3CostDto'
            - type: 'null'
          description: 통화 비용입니다(KRW 원 단위, Decimal 문자열). 계산 완료 전에는 null입니다.
        variant_label:
          anyOf:
            - type: string
            - type: 'null'
          title: Variant Label
          description: Traffic split variant label. Split 미사용/우회/폴백 통화는 null.
        opt_out_sensitive_data_storage:
          type: boolean
          title: Opt Out Sensitive Data Storage
          description: 민감정보 저장 거부 여부. true면 transcript/recording/analysis null.
          default: false
        transcript:
          anyOf:
            - items:
                additionalProperties: true
                type: object
              type: array
            - type: 'null'
          title: Transcript
          description: |-
            통화 대화록 (통합 리스트). 항목 role 3종:
            - 'agent'/'user': {role, content, start_at?, end_at?}
            - 'tool_call_invocation': {role, tool_call_id, name, arguments}
            - 'tool_call_result': {role, tool_call_id, content}
            옵트아웃 시 null.
      type: object
      required:
        - id
        - agent
        - call_type
        - status
      title: CallResponse
      description: v3 단건 통화 응답. transcript 포함.
    ErrorResponse:
      type: object
      required:
        - error
      title: ErrorResponse
      description: 모든 실패 응답에서 사용하는 v3 error envelope입니다.
      examples:
        - error:
            code: VALIDATION_ERROR
            message: Request validation failed.
            details:
              field: name
              reason: must not be blank
      properties:
        error:
          $ref: '#/components/schemas/ErrorDetail'
          description: 오류 payload입니다.
    AgentMapping:
      additionalProperties: false
      description: >-
        `agent_id`와 `agent_version` 매핑입니다.


        요청·응답 모두에서 에이전트 매핑이 필요한 경우 이 타입을 사용합니다. version 지정이 필요 없는 엔드포인트는 flat
        `agent_id: UUID`를 직접 받고 `AgentMapping`을 사용하지 않습니다.
      properties:
        agent_id:
          description: 에이전트 UUID입니다.
          format: uuid
          title: Agent Id
          type: string
        agent_version:
          default: current
          description: >-
            에이전트 버전입니다. 허용값은 "current", "production", "v{n}"(n≥1)입니다. 보내지 않으면
            "current"로 처리합니다. null 전송은 거부합니다.
          title: Agent Version
          type: string
          pattern: ^(current|production|v[1-9][0-9]*)$
          examples:
            - current
            - production
            - v1
      required:
        - agent_id
      title: AgentMapping
      type: object
    CallTypeV3Enum:
      type: string
      enum:
        - web
        - phone
        - inbound
        - outbound
        - api
      title: CallTypeV3Enum
      description: 통화 유형. 5값 중 하나.
    CallStatusV3Enum:
      type: string
      enum:
        - queued
        - ongoing
        - ended
        - error
        - canceled
        - not_connected
      title: CallStatusV3Enum
      description: >-
        통화 상태 (6값).


        값별 의미:

        - `queued`     : 통화 생성 직후 ~ 연결 시도 중인 모든 대기 상태.

        - `ongoing`    : 통화가 실제로 연결되어 진행 중.

        - `ended`      : 정상 종료 (terminal).

        - `error`      : 오류로 종료 — 발신 실패(`dial_failed`), 진행 중 오류 등 (terminal).

        - `canceled`   : 사용자/시스템에 의해 취소됨 (terminal).

        - `not_connected` : 상대가 받지 않거나 SIP 단계에서 연결 실패 (terminal).


        상태 전이 (이 enum 의 변화는 항상 아래 그래프 안에서만 일어난다):

            queued ──▶ ongoing ──▶ ended
                  │            │─▶ error
                  │            │─▶ canceled
                  │            │─▶ not_connected
                  │
                  ├─▶ error              (예: dial_failed)
                  ├─▶ canceled           (연결 전 취소)
                  └─▶ not_connected      (SIP/상대 무응답)

        - 비-terminal 상태: `queued`, `ongoing` (이 두 값에서만 다른 상태로 전이 가능).

        - terminal 상태: `ended`, `error`, `canceled`, `not_connected`
          (이 네 값에 도달하면 다시 변하지 않음, 모든 webhook/조회는 이 값을 최종으로 본다).
        - `start_at` 은 `queued → ongoing` 전이 시점에 채워진다.

        - `end_at` 은 terminal 진입 시점에 채워진다 (`ongoing` 을 거치지 않은 경우 `start_at` 은
        null 인 채로 `end_at` 만 세팅됨).
    DisconnectionReasonDto:
      type: string
      enum:
        - user_hangup
        - agent_hangup
        - dial_no_answer
        - dial_failed
        - dial_busy
        - voicemail_reached
        - concurrency_limit_reached
        - insufficient_balance
        - error_voxai
        - max_duration_reached
        - inactivity
        - flow_error
        - call_transfer
        - orphaned_no_agent_pickup
        - dial_declined
        - daily_call_limit_reached
        - hourly_call_limit_reached
        - blocked_number
      title: DisconnectionReasonDto
      description: 통화 종료 사유 enum입니다.
    CallV3AnalysisDto:
      properties:
        summary:
          anyOf:
            - type: string
            - type: 'null'
          title: Summary
          description: 통화 내용 요약. 분석이 완료되지 않았으면 null.
        user_sentiment:
          anyOf:
            - $ref: '#/components/schemas/UserSentimentDto'
            - type: 'null'
          description: 사용자 감정 분석 결과. 분석이 완료되지 않았으면 null.
        custom_analysis_data:
          anyOf:
            - items:
                additionalProperties: true
                type: object
              type: array
            - type: 'null'
          title: Custom Analysis Data
          description: 사용자 정의 분석 데이터 목록입니다. 데이터가 없으면 null입니다.
      type: object
      title: CallV3AnalysisDto
      description: 통화 분석 결과.
    CallV3CostDto:
      properties:
        total_cost:
          type: string
          title: Total Cost
          description: '통화 총 비용입니다(KRW 원 단위). 소수점 정밀도 유지를 위해 문자열로 전달됩니다. 예: ''123.45''.'
      type: object
      required:
        - total_cost
      title: CallV3CostDto
      description: 통화 비용 정보.
    ErrorDetail:
      type: object
      required:
        - code
        - message
        - details
      title: ErrorDetail
      description: 기계가 읽을 수 있는 v3 오류 상세 정보입니다.
      examples:
        - code: VALIDATION_ERROR
          message: Request validation failed.
          details:
            field: name
            reason: must not be blank
      properties:
        code:
          type: string
          title: Code
          description: 기계가 읽을 수 있는 오류 code입니다. message parsing 대신 이 값을 사용합니다.
          examples:
            - VALIDATION_ERROR
        message:
          type: string
          title: Message
          description: 사용자에게 표시할 수 있는 오류 메시지입니다. 프로그램 처리는 `code`를 사용합니다.
          examples:
            - Request validation failed.
        details:
          type: object
          title: Details
          additionalProperties: true
          description: 구조화된 context입니다. 주로 `field`, `reason`, `allowed_values`를 포함합니다.
    UserSentimentDto:
      type: string
      enum:
        - positive
        - negative
        - neutral
        - unknown
      title: UserSentimentDto
      description: 사용자 감정 enum입니다.
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Organization API key
      description: '조직 API 키를 `Authorization: Bearer <token>` 형식으로 보냅니다.'

````