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

# Send a message to an existing chat

> Send a Message to an existing Chat.

Blocking is silent on this path. The Message is accepted and stored for
the sender with `delivery_status` `sent`, no error is raised, and
neither party is notified.

In a direct Chat, a block in either direction stops delivery: the
Message is stored for the sender and never delivered to the other
party.

In a group Chat, blocking is one way. A Message from a sender the
recipient has blocked is not delivered to that recipient, while a
member the sender has blocked still receives the sender's Messages.
Every other member receives the Message as normal.

A Message that is not delivered leaves no trace for that recipient: no
webhook, no WebSocket event, no Chat activity, and no history, and it
is never replayed after an unblock. The sender's `message.delivered`
fires when at least one recipient was delivered.



## OpenAPI

````yaml /api-reference/openapi.mint.yaml post /v1/chats/{chatId}/messages
openapi: 3.1.0
info:
  title: Relay API
  version: 1.0.0
  description: >-
    Create conversations between users and agents.


    Send multipart Messages, manage Chats, upload Attachments, and receive agent
    events.
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
servers:
  - url: https://api.relayapp.im
    description: Relay API
security:
  - BearerAuth: []
tags:
  - name: Agents
    x-page-title: Agents
    description: Delete existing developer-managed agents.
  - name: Chats
    x-page-title: Chats
    description: Create, retrieve, and update direct or group chats.
  - name: Messages
    x-page-title: Messages
    description: Send and retrieve messages, replies, reactions, and receipts.
  - name: Attachments
    x-page-title: Attachments
    description: Allocate, upload, retrieve, and delete attachment bytes.
  - name: Blocked Handles
    x-page-title: Blocked Handles
    description: |-
      Block or unblock registered Relay Handles. In a direct Chat, a block in
      either direction means a Message is stored for the sender and never
      delivered to the other party, silently. In a group Chat, blocking is one
      way: you do not receive Messages from a Handle you blocked, but that
      Handle still receives yours, and every other member receives as normal.
      Blocking does not delete existing Chats or history.
  - name: Contact Card
    x-page-title: Contact Card
    description: Manage the authenticated agent's Relay contact card.
  - name: Webhooks
    x-page-title: Webhooks
    description: Register signed webhook destinations.
  - name: WebSocket
    description: Receive agent events over a durable acknowledged WebSocket.
  - name: Contacts
    description: Request a user Contact.
paths:
  /v1/chats/{chatId}/messages:
    post:
      tags:
        - Messages
      summary: Send a message to an existing chat
      description: |-
        Send a Message to an existing Chat.

        Blocking is silent on this path. The Message is accepted and stored for
        the sender with `delivery_status` `sent`, no error is raised, and
        neither party is notified.

        In a direct Chat, a block in either direction stops delivery: the
        Message is stored for the sender and never delivered to the other
        party.

        In a group Chat, blocking is one way. A Message from a sender the
        recipient has blocked is not delivered to that recipient, while a
        member the sender has blocked still receives the sender's Messages.
        Every other member receives the Message as normal.

        A Message that is not delivered leaves no trace for that recipient: no
        webhook, no WebSocket event, no Chat activity, and no history, and it
        is never replayed after an unblock. The sender's `message.delivered`
        fires when at least one recipient was delivered.
      operationId: sendMessageToChat
      parameters:
        - name: chatId
          in: path
          required: true
          description: Unique identifier of the chat
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          required: false
          description: >-
            Optional 1–255 character key for safe retries. If
            message.idempotency_key is also present, the two values must match.
          schema:
            type: string
            minLength: 1
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendMessageToChatRequest'
      responses:
        '202':
          description: Message accepted for delivery
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SendMessageResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: |-
            The send is not permitted. A Chat whose member set has no agent in
            it is rejected with error code `2028`. Who may reach whom was
            decided when the Chat was created; every active member may write
            to it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                chat_without_agent:
                  summary: A chat must include an agent
                  value:
                    error:
                      status: 403
                      code: 2028
                      message: A chat must include an agent.
                      doc_url: https://docs.relayapp.im/error/codes/2xxx/2028
                    success: false
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: >-
            Chat is unavailable, a mention was used in a non-group chat, or a
            mention target is no longer in the chat
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/InternalServerError'
      security:
        - BearerAuth: []
components:
  schemas:
    SendMessageToChatRequest:
      type: object
      required:
        - message
      properties:
        message:
          $ref: '#/components/schemas/MessageContent'
    SendMessageResponse:
      type: object
      description: Response for sending a message to a chat
      required:
        - chat_id
        - message
      properties:
        chat_id:
          type: string
          format: uuid
          description: Unique identifier of the chat this message was sent to
        message:
          $ref: '#/components/schemas/SentMessage'
    ErrorResponse:
      type: object
      required:
        - error
        - success
      properties:
        error:
          $ref: '#/components/schemas/ErrorDetail'
        success:
          type: boolean
          description: Always false for error responses
        trace_id:
          type: string
          description: Unique trace ID for request tracing and debugging
    MessageContent:
      type: object
      description: >-
        One message containing 1–100 ordered text, media, or link parts. A link
        must be the only part.
      properties:
        parts:
          x-go-type-skip-optional-pointer: true
          type: array
          minItems: 1
          maxItems: 100
          items:
            $ref: '#/components/schemas/MessagePart'
        reply_to:
          $ref: '#/components/schemas/ReplyTo'
          description: Reply to another message to create a threaded conversation
        idempotency_key:
          type: string
          description: >-
            Optional key for a safe retry. The same authenticated sender, key,
            and Message body return the original Message. Reusing the key with a
            different body returns a conflict.
          maxLength: 255
        silent:
          type: boolean
          default: false
          description: >-
            Send the Message with no banner and no sound on the recipient's
            device. The Message still arrives, still counts as unread, and still
            moves the Chat to the top of the list.
      required:
        - parts
    SentMessage:
      type: object
      description: A message that was sent (used in CreateChat and SendMessage responses)
      required:
        - id
        - parts
        - created_at
        - sent_at
        - delivery_status
      properties:
        id:
          type: string
          format: uuid
          description: Message identifier (UUID)
        parts:
          type: array
          description: Message parts in order (text, media, and link)
          items:
            oneOf:
              - $ref: '#/components/schemas/TextPartResponse'
              - $ref: '#/components/schemas/MediaPartResponse'
              - $ref: '#/components/schemas/LinkPartResponse'
        created_at:
          type: string
          format: date-time
          description: When the message was created
        sent_at:
          type:
            - string
            - 'null'
          format: date-time
          description: When the message was actually sent (null if still queued)
        delivered_at:
          type:
            - string
            - 'null'
          format: date-time
          description: When Relay accepted and stored the Message.
        delivery_status:
          $ref: '#/components/schemas/DeliveryStatus'
        from_handle:
          description: The sender of this message as a full handle object
          oneOf:
            - $ref: '#/components/schemas/ChatHandle'
            - type: 'null'
        silent:
          type: boolean
          description: >-
            Whether the sender sent this Message silently, so the recipient's
            device showed no banner and played no sound.
        reply_to:
          oneOf:
            - $ref: '#/components/schemas/ReplyTo'
            - type: 'null'
        thread:
          description: The thread this Message belongs to, or null when it is not a reply.
          oneOf:
            - $ref: '#/components/schemas/ThreadOriginator'
            - type: 'null'
    ErrorDetail:
      type: object
      required:
        - status
        - code
        - message
        - doc_url
      properties:
        status:
          type: integer
          description: HTTP status code (e.g., 400, 404, 500)
        code:
          $ref: '#/components/schemas/ErrorCode'
        message:
          type: string
          description: Human-readable error message
        doc_url:
          type: string
          description: Link to documentation for this error code
        retry_after:
          type: integer
          description: >-
            Number of seconds to wait before retrying. Only present on 429 rate
            limit errors.
    MessagePart:
      type: object
      required:
        - type
      discriminator:
        propertyName: type
        mapping:
          text:
            $ref: '#/components/schemas/TextPart'
          media:
            $ref: '#/components/schemas/MediaPart'
          link:
            $ref: '#/components/schemas/LinkPart'
      oneOf:
        - $ref: '#/components/schemas/TextPart'
        - $ref: '#/components/schemas/MediaPart'
        - $ref: '#/components/schemas/LinkPart'
    ReplyTo:
      type: object
      description: Indicates this message is a threaded reply to another message
      required:
        - message_id
      properties:
        message_id:
          type: string
          format: uuid
          description: The ID of the message to reply to
        part_index:
          type: integer
          format: int32
          description: |
            The specific message part to reply to (0-based index).
            Defaults to 0 (first part) if not provided.
            Use this when replying to a specific part of a multipart message.
          minimum: 0
    TextPartResponse:
      type: object
      description: A text message part
      required:
        - type
        - value
        - reactions
      properties:
        type:
          type: string
          enum:
            - text
          description: Indicates this is a text message part
        value:
          type: string
          description: The text content
        mentions:
          type:
            - array
            - 'null'
          description: >-
            Mentions ordered by position in the text; null when none. Handles
            reflect the participant's current handle.
          items:
            type: object
            required:
              - id
              - handle
              - is_me
              - range
            properties:
              id:
                type: string
                format: uuid
                description: >-
                  Stable participant Contact ID, unchanged when the handle
                  changes.
              handle:
                type: string
                description: The target participant's current Relay Handle.
              is_me:
                type: boolean
                description: >-
                  Whether the mention addresses the recipient reading this
                  message.
              range:
                type: array
                items:
                  type: integer
                  minimum: 0
                minItems: 2
                maxItems: 2
                description: >-
                  Inclusive-exclusive [start, end) UTF-16 code-unit range in
                  value; the whole value when the send omitted mention_range.
        mention:
          deprecated: true
          type:
            - string
            - 'null'
          description: Deprecated mirror of the first mention handle.
        mention_range:
          deprecated: true
          type:
            - array
            - 'null'
          prefixItems:
            - type: integer
              minimum: 0
            - type: integer
              minimum: 1
          minItems: 2
          maxItems: 2
          description: >-
            Deprecated mirror of the first mention range; null when omitted on
            send.
        reactions:
          type:
            - array
            - 'null'
          description: Reactions on this message part
          items:
            $ref: '#/components/schemas/Reaction'
    MediaPartResponse:
      type: object
      description: A media attachment part
      required:
        - type
        - id
        - url
        - filename
        - mime_type
        - size_bytes
        - reactions
      properties:
        type:
          type: string
          enum:
            - media
          description: Indicates this is a media attachment part
        id:
          type: string
          format: uuid
          description: Unique attachment identifier
        url:
          type: string
          format: uri
          description: Opaque Relay download URL for the Attachment.
        filename:
          type: string
          description: Original filename
        mime_type:
          type: string
          description: MIME type of the file
        size_bytes:
          type: integer
          description: File size in bytes
        reactions:
          type:
            - array
            - 'null'
          description: Reactions on this message part
          items:
            $ref: '#/components/schemas/Reaction'
        duration_ms:
          type:
            - integer
            - 'null'
          minimum: 0
          description: Audio or video duration in milliseconds when known.
        width:
          type:
            - integer
            - 'null'
          minimum: 1
          description: Pixel width when known. Width and height are supplied together.
        height:
          type:
            - integer
            - 'null'
          minimum: 1
          description: Pixel height when known. Width and height are supplied together.
    LinkPartResponse:
      type: object
      description: A rich link preview part
      required:
        - type
        - value
        - reactions
      properties:
        type:
          type: string
          enum:
            - link
          description: Indicates this is a rich link preview part
        value:
          type: string
          description: The URL
        reactions:
          type:
            - array
            - 'null'
          description: Reactions on this message part
          items:
            $ref: '#/components/schemas/Reaction'
    DeliveryStatus:
      type: string
      description: >-
        Current receipt state. Sent is a client-only handoff state. Delivered
        means Relay accepted and stored the Message. Read means every recipient
        explicitly marked the Chat Read.
      enum:
        - sent
        - delivered
        - read
    ChatHandle:
      type: object
      required:
        - id
        - handle
        - joined_at
        - kind
        - display_name
        - image_url
        - image_color
        - about
        - verified
        - is_contact
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier for this handle
        handle:
          type: string
          description: Relay Handle.
        status:
          type:
            - string
            - 'null'
          description: Participant status
          enum:
            - active
            - left
            - removed
          default: active
        joined_at:
          type: string
          format: date-time
          description: When this participant joined the chat
        left_at:
          type:
            - string
            - 'null'
          format: date-time
          description: When they left (if applicable)
        is_me:
          type:
            - boolean
            - 'null'
          description: Whether this handle is the authenticated caller.
        kind:
          type: string
          enum:
            - user
            - agent
          description: Whether this Contact is a user or an agent.
        display_name:
          type:
            - string
            - 'null'
          description: Current Contact display name.
        image_url:
          type:
            - string
            - 'null'
          format: uri
          description: >-
            Current Contact picture, as a permanent address served by Relay. It
            does not expire and may be cached indefinitely.
        image_color:
          type:
            - string
            - 'null'
          pattern: ^[0-9A-F]{6}$
          description: >-
            Dominant colour of the picture at image_url, six uppercase hex
            digits, computed by Relay once when the picture is set. Null when
            Relay could not read the picture.
        about:
          type:
            - string
            - 'null'
          maxLength: 60
          description: About text for an agent. User Contacts return null.
        verified:
          type: boolean
          description: Whether Relay has verified this agent.
        is_contact:
          type: boolean
          description: >-
            Whether the caller holds this member as a Contact. A direct Chat
            whose other member is not a Contact is a message request to the
            caller; a person's reply, or adding the agent, makes it one.
    ThreadOriginator:
      type: object
      description: >-
        The Message and part that opened a thread. A reply to a reply names the
        same originator as the Message it replied to, so every Message in one
        thread carries the same value. Withheld, as null on the Message, from a
        reader who cannot see the originator.
      required:
        - originator_message_id
        - originator_part_index
      properties:
        originator_message_id:
          type: string
          format: uuid
          description: ID of the Message that opened the thread.
        originator_part_index:
          type: integer
          format: int32
          minimum: 0
          description: Index of the originator's part that opened the thread (0-based).
    ErrorCode:
      type: integer
      description: Relay API error code.
    TextPart:
      type: object
      required:
        - type
        - value
      properties:
        type:
          type: string
          enum:
            - text
          description: Indicates this is a text message part
        value:
          type: string
          description: >-
            Plain text content, measured in UTF-16 code units for mention
            ranges.
          minLength: 1
          maxLength: 10000
        mention:
          type:
            - string
            - 'null'
          description: Relay Handle mentioned by this text part.
        mention_range:
          type:
            - array
            - 'null'
          prefixItems:
            - type: integer
              minimum: 0
            - type: integer
              minimum: 1
          minItems: 2
          maxItems: 2
          description: UTF-16 [start,end) range of the mention inside value.
    MediaPart:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - media
          description: Indicates this is a media attachment part
        url:
          type: string
          format: uri
          description: >-
            Public HTTPS media URL. Relay blocks non-public networks and
            validates every redirect, response status, and declared and actual
            size. Any file type is accepted; a host that declares no usable
            Content-Type is stored as application/octet-stream.
          pattern: ^https://
        attachment_id:
          type: string
          format: uuid
          description: >-
            Reference an Attachment allocated through POST /v1/attachments. Use
            either url or attachment_id, not both.
    LinkPart:
      type: object
      required:
        - type
        - value
      properties:
        type:
          type: string
          enum:
            - link
          description: Indicates this is a rich link preview part
        value:
          type: string
          format: uri
          description: >
            URL to send with a rich link preview. The recipient will see an
            inline card

            with the page's title, description, and preview image (when
            available).


            A `link` part must be the **only** part in the message. To send a
            URL as plain

            text (no preview card), use a `text` part instead.
          minLength: 1
          maxLength: 2048
    Reaction:
      type: object
      required:
        - is_me
        - handle
        - type
      properties:
        is_me:
          type: boolean
          description: Whether this reaction is from the current user
        handle:
          $ref: '#/components/schemas/ChatHandle'
        type:
          $ref: '#/components/schemas/ReactionType'
        custom_emoji:
          type:
            - string
            - 'null'
          description: Custom emoji if type is "custom", null otherwise
    ReactionType:
      type: string
      description: Six standard tapbacks or a custom Unicode emoji.
      enum:
        - love
        - like
        - dislike
        - laugh
        - emphasize
        - question
        - custom
  responses:
    BadRequest:
      description: Invalid request - validation error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: Unauthorized - missing or invalid authentication
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: >
        Bearer token authentication. Include your API token in the Authorization
        header.


        Format: `Authorization: Bearer <your-token>`

````