---
title: "Send a message"
method: POST
path: "/v1/conversations/{id}/messages"
tags: ["Conversations"]
---

# Send a message

`POST /v1/conversations/{id}/messages`

## Request

- Base URL: `https://api.cadencercs.com`
- URL: `https://api.cadencercs.com/v1/conversations/{id}/messages`
- Auth: HTTP bearer

Later turns go out under the same agent as the opening, resolved from the conversation itself rather than from this request. Unknown body fields are rejected rather than ignored.

**A CLOSED conversation is still sendable**, on purpose — delivering a summary after a flow's closing is a real pattern. The response then carries `conversationState: "CLOSED"` and a `warning`, because there are two costs: it bills against the same conversation, so on one the recipient never replied to it can push past the included message count into overage; and Google's 24-hour session runs from the *recipient's* last message, not from our state, so if theirs was over 24 hours ago this opens a new billable session. Open a new conversation if you want a clean record.

A CLOSED_OPTOUT conversation is not sendable, and never becomes so by asserting consent.

## Path parameters

- `id` string, required

## Request body

- SendMessageRequest: Unknown fields are **rejected**, not ignored: someone posted `{"direction":"in","text":"…"}` and got a 200 while the text went out to the recipient as the brand. A typo in a field name must never become a message to a real person.
  - `text` string
  - `richCard` object
  - `media` object
    - `url` string, uri, required
    - `altText` string, required
    - `thumbnailUrl` string, uri
  - `card` object
    - `title` string
    - `description` string
    - `media` object
      - `url` string, uri, required
      - `altText` string, required
      - `thumbnailUrl` string, uri
      - `height` 'SHORT' | 'MEDIUM' | 'TALL'
    - `suggestions` union[]
      - union
        - string
        - object
          - `text` string, required
          - `action` union, required
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
          - `smsFallback` string
    - `orientation` 'VERTICAL' | 'HORIZONTAL'
    - `thumbnailAlignment` 'LEFT' | 'RIGHT'
  - `carousel` object
    - `width` 'SMALL' | 'MEDIUM'
    - `cards` object[], required
      - `title` string
      - `description` string
      - `media` object
        - `url` string, uri, required
        - `altText` string, required
        - `thumbnailUrl` string, uri
        - `height` 'SHORT' | 'MEDIUM' | 'TALL'
      - `suggestions` union[]
        - union
          - string
          - object
            - `text` string, required
            - `action` union, required
              - …
            - `smsFallback` string
  - `suggestions` union[]
    - union
      - string
      - object
        - `text` string, required
        - `action` union, required
          - object
            - `openUrl` string, uri, required
          - object
            - `dial` string, required
          - object
            - `calendar` object, required
              - …
          - object
            - `viewLocation` union, required
              - …
          - object
            - `shareLocation` true, required
        - `smsFallback` string
  - `smsFallbackText` string
  - `idempotencyKey` string
  - `expiresIn` integer
  - `expiresAt` string, date-time

## Response `200`

Sent.

- object
  - `providerMsgId` string, required
  - `channel` 'rcs' | 'sms', required: The leg this message actually went out on. Always present, so a rich send that degraded to text is visible from the response alone.
  - `deliveredText` string: What the recipient received on that leg: the fallback text on SMS, the composed text on RCS.
  - `degraded` object: Present when rich content (media, card, carousel, chips) was sent into a conversation already on SMS. Not a fallback — nothing failed on this send — but the picture went as a link and the chips as a numbered list. `channelConfidence: committed` means the recipient cannot receive RCS; `provisional` means an earlier attempt failed on our side and a later message may go rich again.
    - `at` 'conversation', required
    - `cause` 'channel_sms', required
    - `channelConfidence` 'provisional' | 'committed'
    - `note` string, required
  - `fellBack` FellBack
    - `cause` 'recipient' | 'sender', required: `recipient`: their handset has no RCS, or your agent is not launched on their carrier. `sender`: we were rejected — a bad payload, a media URL Google could not fetch, a timeout. Nothing was learned about the recipient.
    - `reason` string, required: Google's or the carrier's own words, trimmed.
    - `ourFault` boolean, required: True when the fallback says nothing about the recipient. Retrying rich is reasonable once the reason is fixed.
    - `remediation` string
    - `mediaRejected` object: Present when Google refused the message because of the file behind `media.url` or a card's media. The URL must answer GET and HEAD with 200, a real Content-Type and Content-Length, over public https with no login. The recipient is reachable; the SMS carried the fallback text.
      - `url` string, required: The file Google refused.
      - `stage` 'google_fetch', required: Google fetches `media.url` itself; we never see the HTTP exchange.
      - `status` number: The HTTP status Google's error names, when it names one.
      - `reason` string, required: Google's sentence about the file.
  - `conversationState` string: The conversation's state at the time of sending.
  - `warning` string: Present only when sending into a conversation that had already CLOSED — see the description.

## Other responses

- `400`: Validation failed, or the turn ceiling was hit.
- `401`: Missing, unknown or revoked API key.
- `403`: Key suspended, or scoped to a different brand than the one addressed.
- `404`: No conversation with that id on this key or account.
- `409`: The recipient opted out.
- `500`: Unexpected server error. The body is always JSON.

---

[API](https://skmtc.dev/cadencercs/apis/cadence-api.md) · [All operations](https://skmtc.dev/cadencercs/apis/cadence-api/llms.txt) · [OpenAPI document](https://skmtc.dev/cadencercs/apis/cadence-api/revisions/9438443c2081?raw)
