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

# Send a message

> POST /api/v1/comms/messages — place an outbound SMS or iMessage on your line.

Place an outbound message on your Comms line.

**Scope:** `comms_send`

## Request

```bash theme={null}
curl -X POST "https://osis.co/api/v1/comms/messages" \
  -H "Authorization: Bearer $COMMS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-4242-confirm" \
  -d '{
    "to": "+12125550147",
    "body": "Your appointment is confirmed for Tuesday at 3pm."
  }'
```

### Headers

| Header            | Required    | Description                 |
| ----------------- | ----------- | --------------------------- |
| `Authorization`   | Yes         | `Bearer <COMMS_API_KEY>`    |
| `Content-Type`    | Yes         | `application/json`          |
| `Idempotency-Key` | Recommended | Stable key for safe retries |

### Body parameters

<ParamField body="to" type="string">
  E.164 destination phone number. Required unless `conversation_id` is set.
</ParamField>

<ParamField body="conversation_id" type="string">
  Existing conversation to continue. Required unless `to` is set.
</ParamField>

<ParamField body="body" type="string" required>
  Message text delivered to the recipient.
</ParamField>

<ParamField body="channel" type="string">
  Optional preference: `sms` or `imessage`. Omit to let Comms choose.
</ParamField>

<ParamField body="idempotency_key" type="string">
  Same as the `Idempotency-Key` header. Either form is accepted.
</ParamField>

## Response

### 202 Accepted

First time this idempotency key is seen.

```json theme={null}
{
  "message": {
    "id": "msg_01HZX…",
    "body": "Your appointment is confirmed for Tuesday at 3pm.",
    "direction": "outbound"
  }
}
```

### 200 OK (duplicate)

Same idempotency key already processed — no second send.

```json theme={null}
{
  "message": {
    "id": "msg_01HZX…",
    "body": "Your appointment is confirmed for Tuesday at 3pm.",
    "direction": "outbound"
  },
  "duplicate": true
}
```

### Errors

| Status | Example body                                     | Cause                |
| ------ | ------------------------------------------------ | -------------------- |
| 401    | `{ "error": "unauthorized" }`                    | Bad or missing key   |
| 403    | `{ "error": "missing comms_send scope" }`        | Key lacks send       |
| 429    | `{ "error": "rate_limited", "retry_after": 60 }` | Send or global limit |

## Code samples

<CodeGroup>
  ```typescript Node theme={null}
  await fetch("https://osis.co/api/v1/comms/messages", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.COMMS_API_KEY!}`,
      "Content-Type": "application/json",
      "Idempotency-Key": `order-${orderId}-confirm`,
    },
    body: JSON.stringify({
      to: customerPhone,
      body: "Your appointment is confirmed for Tuesday at 3pm.",
    }),
  });
  ```

  ```python Python theme={null}
  requests.post(
      "https://osis.co/api/v1/comms/messages",
      headers={
          "Authorization": f"Bearer {os.environ['COMMS_API_KEY']}",
          "Content-Type": "application/json",
          "Idempotency-Key": f"order-{order_id}-confirm",
      },
      json={
          "to": customer_phone,
          "body": "Your appointment is confirmed for Tuesday at 3pm.",
      },
  )
  ```
</CodeGroup>

## See also

* [Idempotency](/guides/idempotency)
* [List messages](/messages-api/list-messages)
* [Errors & rate limits](/guides/errors-and-rate-limits)
