> ## 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.

# 채팅 outbound 메시지 전송

> 에이전트를 실행하지 않고 발신 채팅 메시지 한 건을 생성한 뒤 지원되는 채널로 전달합니다. origin=human은 Supabase JWT와 X-Vox-Organization-Id 헤더가 필요하며, 호출자가 활성 상담원이자 해당 Chat의 현재 응답자여야 합니다. origin=system은 조직 API key 또는 내부 관리자 인증이 필요합니다.



## OpenAPI

````yaml /api-reference/v3/openapi.json post /chats/{chat_id}/outbound-messages
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:
  /chats/{chat_id}/outbound-messages:
    post:
      tags:
        - Chats
      summary: 채팅 outbound 메시지 전송
      description: >-
        에이전트를 실행하지 않고 발신 채팅 메시지 한 건을 생성한 뒤 지원되는 채널로 전달합니다. origin=human은
        Supabase JWT와 X-Vox-Organization-Id 헤더가 필요하며, 호출자가 활성 상담원이자 해당 Chat의 현재
        응답자여야 합니다. origin=system은 조직 API key 또는 내부 관리자 인증이 필요합니다.
      operationId: createChatOutboundMessage
      parameters:
        - name: chat_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
            title: Chat Id
        - name: X-Vox-Chat-Turn-Id
          in: header
          required: false
          schema:
            anyOf:
              - type: integer
                maximum: 9223372036854776000
                minimum: 1
              - type: 'null'
            title: X-Vox-Chat-Turn-Id
        - name: X-Vox-Organization-Id
          in: header
          required: false
          description: >-
            Supabase JWT 인증을 사용할 때 필수인 organization UUID입니다. origin=human은 이 인증
            경로만 허용합니다.
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateChatOutboundMessageRequest'
        description: 이 엔드포인트의 요청 데이터입니다.
      responses:
        '201':
          description: 성공 응답
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateChatOutboundMessageResponse'
        '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: CHAT_NOT_FOUND
                      message: 채팅을 찾을 수 없습니다.
                      details:
                        chat_id: 22222222-2222-4222-8222-222222222222
        '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
        '413':
          description: 요청 본문이 너무 큽니다.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                payloadTooLarge:
                  summary: Chat JSON 본문이 10MiB 제한을 초과했습니다.
                  value:
                    error:
                      code: PAYLOAD_TOO_LARGE
                      message: 요청 본문이 허용 크기를 초과했습니다.
                      details:
                        content_length: 11534336
                        max_bytes: 10485760
        '422':
          description: 요청 검증 오류
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                unprocessableEntity:
                  summary: 처리할 수 없는 요청입니다.
                  value:
                    error:
                      code: CONVERTED_FILE_TOO_LARGE
                      message: Converted image size exceeds the 1 MB limit.
                      details:
                        converted_size_bytes: 1258291
                        max_bytes: 1048576
        '429':
          description: >-
            요청 한도를 초과했습니다. API 키 트래픽은 기본값으로 organization당 초당 5회·분당 120회,
            Supabase JWT 트래픽은 기본값으로 사용자당 초당 10회·분당 240회로 제한되며, organization 콘솔에는
            기본 분당 2,400회의 백스톱 한도가 적용됩니다. 한도는 운영자가 조정할 수 있고, 초과한 버킷의 실제 한도와 윈도우는
            429 응답의 X-RateLimit-Limit·X-RateLimit-Window 헤더로 알려줍니다. 개별 경로 자체
            리미터가 만든 429 응답에는 이 헤더가 없을 수 있습니다.
          headers:
            Retry-After:
              description: >-
                초과한 공용 v3 인증 주체 리미터의 윈도우가 초기화될 때까지 남은 시간입니다. 단위는 초입니다. 개별 경로의 자체
                리미터가 만든 429 응답에는 이 헤더가 없을 수 있습니다.
              schema:
                type: integer
                minimum: 1
            X-RateLimit-Limit:
              description: >-
                초과한 공용 v3 인증 주체 리미터 윈도우의 최대 요청 수입니다. 개별 경로의 자체 리미터가 만든 429 응답에는
                이 헤더가 없을 수 있습니다.
              schema:
                type: integer
                minimum: 1
            X-RateLimit-Window:
              description: >-
                초과한 공용 v3 인증 주체 리미터의 윈도우 길이입니다. 단위는 초입니다. 개별 경로의 자체 리미터가 만든 429
                응답에는 이 헤더가 없을 수 있습니다.
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                rateLimited:
                  summary: 요청 한도를 초과했습니다.
                  value:
                    error:
                      code: RATE_LIMIT_EXCEEDED
                      message: Too many requests.
                      details:
                        limit: 10
                        window_seconds: 1
                        scope: user
        '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: []
        - SupabaseJwtBearer: []
components:
  schemas:
    CreateChatOutboundMessageRequest:
      properties:
        origin:
          type: string
          enum:
            - human
            - system
            - tool
          title: Origin
          description: >-
            메시지를 만든 외부 주체입니다. Public role로 노출하지 않습니다. human은 Supabase JWT와
            X-Vox-Organization-Id 헤더가 필요하며, 활성 상담원이 현재 responder인 Chat에서만 사용할 수
            있습니다. system은 organization API key 또는 내부 admin만 사용할 수 있습니다.
          examples:
            - human
        text:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          title: Text
          description: assistant-side outbound 메시지 텍스트입니다.
        images:
          anyOf:
            - items:
                oneOf:
                  - $ref: '#/components/schemas/ChatMessageFileKeyImageInput'
                  - $ref: '#/components/schemas/ChatMessageBase64ImageInput'
                  - $ref: '#/components/schemas/ChatMessageUrlImageInput'
                discriminator:
                  propertyName: type
                  mapping:
                    base64:
                      $ref: '#/components/schemas/ChatMessageBase64ImageInput'
                    file_key:
                      $ref: '#/components/schemas/ChatMessageFileKeyImageInput'
                    url:
                      $ref: '#/components/schemas/ChatMessageUrlImageInput'
              type: array
              maxItems: 3
            - type: 'null'
          title: Images
          description: 선택 이미지 입력입니다. 최대 3장까지 지원합니다.
        navertalk:
          anyOf:
            - $ref: '#/components/schemas/NavertalkContent'
            - type: 'null'
          description: 네이버 전용 TEXT/LINK 버튼 또는 최대 10개 카드. text/images와 함께 지정할 수 없습니다.
        client_idempotency_key:
          type: string
          maxLength: 128
          minLength: 1
          title: Client Idempotency Key
          description: >-
            클라이언트가 생성한 outbound 메시지 멱등 키입니다. `responder-notice` namespace는 내부
            전용이라 사용할 수 없습니다.
      additionalProperties: false
      type: object
      required:
        - origin
        - client_idempotency_key
      title: CreateChatOutboundMessageRequest
    CreateChatOutboundMessageResponse:
      properties:
        id:
          type: integer
          title: Id
          description: 저장된 assistant 메시지 ID입니다.
        chat_id:
          type: string
          title: Chat Id
          description: 채팅 ID입니다.
        role:
          type: string
          const: assistant
          title: Role
          default: assistant
        content:
          type: string
          title: Content
          description: 저장된 assistant 메시지 텍스트입니다.
        attachments:
          anyOf:
            - items:
                $ref: '#/components/schemas/ChatMessageAttachment'
              type: array
            - type: 'null'
          title: Attachments
          description: 저장된 이미지 첨부 목록입니다.
        delivery:
          $ref: '#/components/schemas/ChatOutboundDeliveryResponse'
          description: 채널 발송 결과입니다.
      type: object
      required:
        - id
        - chat_id
        - content
        - delivery
      title: CreateChatOutboundMessageResponse
    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입니다.
    ChatMessageFileKeyImageInput:
      properties:
        type:
          type: string
          const: file_key
          title: Type
          description: 기존 업로드 파일 키를 사용하는 이미지 입력입니다.
        file_key:
          type: string
          minLength: 1
          title: File Key
          description: '`POST /files`에서 받은 기존 업로드 파일 키입니다.'
          examples:
            - file_abc123
      type: object
      required:
        - type
        - file_key
      title: ChatMessageFileKeyImageInput
    ChatMessageBase64ImageInput:
      properties:
        type:
          type: string
          const: base64
          title: Type
          description: base64 데이터를 직접 보내는 이미지 입력입니다.
        mime_type:
          anyOf:
            - type: string
              minLength: 1
            - type: 'null'
          title: Mime Type
          description: 클라이언트가 선언한 MIME 타입입니다.
          examples:
            - image/jpeg
        data:
          type: string
          minLength: 1
          title: Data
          description: base64 이미지 데이터입니다.
      type: object
      required:
        - type
        - data
      title: ChatMessageBase64ImageInput
    ChatMessageUrlImageInput:
      properties:
        type:
          type: string
          const: url
          title: Type
          description: 서버가 HTTPS URL에서 가져오는 이미지 입력입니다.
        url:
          type: string
          minLength: 1
          title: Url
          description: 서버가 fetch할 HTTPS 이미지 URL입니다.
          examples:
            - https://example.com/photo.jpg
      type: object
      required:
        - type
        - url
      title: ChatMessageUrlImageInput
    NavertalkContent:
      oneOf:
        - properties:
            text_content:
              type: object
            composite_content:
              type: 'null'
          required:
            - text_content
        - properties:
            text_content:
              type: 'null'
            composite_content:
              type: object
          required:
            - composite_content
      properties:
        text_content:
          anyOf:
            - $ref: '#/components/schemas/NavertalkTextContent'
            - type: 'null'
        composite_content:
          anyOf:
            - $ref: '#/components/schemas/NavertalkCompositeContent'
            - type: 'null'
      additionalProperties: false
      type: object
      title: NavertalkContentDto
    ChatMessageAttachment:
      properties:
        file_key:
          type: string
          title: File Key
          description: 첨부 파일 키입니다.
        mime_type:
          type: string
          title: Mime Type
          description: 첨부 MIME 타입입니다.
      type: object
      required:
        - file_key
        - mime_type
      title: ChatMessageAttachment
    ChatOutboundDeliveryResponse:
      properties:
        channel:
          anyOf:
            - type: string
              enum:
                - api
                - widget
                - sms
                - kakao
            - type: string
          title: Channel
          description: 발송 대상 chat channel입니다.
        status:
          type: string
          enum:
            - stored
            - pending
            - sent
            - failed
          title: Status
          description: >-
            채널 발송 결과입니다. API/widget chat은 transport가 없어 stored이고, Kakao는
            provider 접수 후 DLR 확정 전까지 pending입니다.
        provider_message_ids:
          items:
            type: string
          type: array
          title: Provider Message Ids
          description: 채널 provider/outbox 메시지 식별자입니다.
        error_code:
          anyOf:
            - type: string
            - type: 'null'
          title: Error Code
          description: 발송 실패 시 machine-readable 에러 코드입니다.
        error_message:
          anyOf:
            - type: string
            - type: 'null'
          title: Error Message
          description: 발송 실패 시 사람이 읽을 수 있는 에러 메시지입니다.
      type: object
      required:
        - channel
        - status
      title: ChatOutboundDeliveryResponse
    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`를 포함합니다.
    NavertalkTextContent:
      properties:
        text:
          type: string
          maxLength: 10000
          minLength: 1
          title: Text
        quick_reply:
          anyOf:
            - $ref: '#/components/schemas/NavertalkQuickReply'
            - type: 'null'
      additionalProperties: false
      type: object
      required:
        - text
      title: NavertalkTextContent
    NavertalkCompositeContent:
      properties:
        composite_list:
          items:
            $ref: '#/components/schemas/NavertalkComposite'
          type: array
          maxItems: 10
          minItems: 1
          title: Composite List
      additionalProperties: false
      type: object
      required:
        - composite_list
      title: NavertalkCompositeContent
    NavertalkQuickReply:
      properties:
        button_list:
          items:
            oneOf:
              - $ref: '#/components/schemas/NavertalkTextButton'
              - $ref: '#/components/schemas/NavertalkLinkButton'
            discriminator:
              propertyName: type
              mapping:
                LINK:
                  $ref: '#/components/schemas/NavertalkLinkButton'
                TEXT:
                  $ref: '#/components/schemas/NavertalkTextButton'
          type: array
          maxItems: 10
          minItems: 1
          title: Button List
          description: 각 버튼의 data.title은 10자 이하여야 합니다.
      additionalProperties: false
      type: object
      required:
        - button_list
      title: NavertalkQuickReply
    NavertalkComposite:
      properties:
        title:
          anyOf:
            - type: string
              maxLength: 200
              minLength: 1
            - type: 'null'
          title: Title
        description:
          anyOf:
            - type: string
              maxLength: 1000
              minLength: 1
            - type: 'null'
          title: Description
          description: 사람이 읽을 수 있는 설명입니다.
        image:
          anyOf:
            - $ref: '#/components/schemas/NavertalkImage'
            - type: 'null'
        button_list:
          anyOf:
            - items:
                oneOf:
                  - $ref: '#/components/schemas/NavertalkTextButton'
                  - $ref: '#/components/schemas/NavertalkLinkButton'
                discriminator:
                  propertyName: type
                  mapping:
                    LINK:
                      $ref: '#/components/schemas/NavertalkLinkButton'
                    TEXT:
                      $ref: '#/components/schemas/NavertalkTextButton'
              type: array
              maxItems: 10
              minItems: 1
            - type: 'null'
          title: Button List
      additionalProperties: false
      type: object
      title: NavertalkComposite
    NavertalkTextButton:
      properties:
        type:
          type: string
          const: TEXT
          title: Type
          description: 리소스 또는 설정 유형입니다.
        data:
          $ref: '#/components/schemas/NavertalkTextButtonData'
          description: 에이전트 설정 데이터입니다. 이 객체는 camelCase 필드명을 사용합니다.
      additionalProperties: false
      type: object
      required:
        - type
        - data
      title: NavertalkTextButton
    NavertalkLinkButton:
      properties:
        type:
          type: string
          const: LINK
          title: Type
          description: 리소스 또는 설정 유형입니다.
        data:
          $ref: '#/components/schemas/NavertalkLinkButtonData'
          description: 에이전트 설정 데이터입니다. 이 객체는 camelCase 필드명을 사용합니다.
      additionalProperties: false
      type: object
      required:
        - type
        - data
      title: NavertalkLinkButton
    NavertalkImage:
      properties:
        file_key:
          type: string
          maxLength: 255
          minLength: 1
          title: File Key
      additionalProperties: false
      type: object
      required:
        - file_key
      title: NavertalkImage
    NavertalkTextButtonData:
      properties:
        title:
          type: string
          maxLength: 18
          minLength: 1
          title: Title
        code:
          anyOf:
            - type: string
              maxLength: 1000
              minLength: 1
            - type: 'null'
          title: Code
      additionalProperties: false
      type: object
      required:
        - title
      title: NavertalkTextButtonData
    NavertalkLinkButtonData:
      properties:
        title:
          type: string
          maxLength: 18
          minLength: 1
          title: Title
        url:
          type: string
          maxLength: 2083
          minLength: 1
          format: uri
          title: Url
        mobile_url:
          type: string
          maxLength: 2083
          minLength: 1
          format: uri
          title: Mobile Url
      additionalProperties: false
      type: object
      required:
        - title
        - url
        - mobile_url
      title: NavertalkLinkButtonData
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Organization API key
      description: '조직 API 키를 `Authorization: Bearer <token>` 형식으로 보냅니다.'
    SupabaseJwtBearer:
      type: http
      scheme: bearer
      bearerFormat: Supabase access token
      description: >-
        인증된 대시보드 사용자의 Supabase access token을 `Authorization: Bearer <token>`
        형식으로 `X-Vox-Organization-Id`와 함께 보냅니다. 조직 API 키는 이 인증 방식을 대신할 수 없습니다.

````