# Sending messages

URL: https://wa.genuka.com/en/docs/messages
Language: English

> One endpoint covering the whole Cloud API surface.

```http
POST /api/v1/messages
```

A send always carries three things: the connection to send from, the recipient, and **exactly one
content field**.

```json
{
  "connectionId": "cnx_...",
  "to": "+237699001122",
  "text": "Hello 👋"
}
```

The number is in international format. Get `connectionId` from `GET /api/v1/connections`. Add
`replyTo` with the `wamid` of an inbound message to quote it in your reply.

## Content fields

> [!WARNING]
> **One field at a time**
>
> Sending two content fields in the same request does not send two messages: only the first
> recognised one is used. Make two calls.

### Text and media

| Field      | Content                             | Limits                        |
| ---------- | ----------------------------------- | ----------------------------- |
| `text`     | A string, or `{ body, previewUrl }` | 4096 characters               |
| `image`    | JPEG, PNG                           | 5 MB, caption 1024 characters |
| `video`    | MP4, 3GPP (H.264 + AAC)             | 16 MB                         |
| `audio`    | AAC, AMR, MP3, M4A, OGG             | 16 MB, **no caption**         |
| `document` | PDF, DOC(X), XLS(X), PPT(X), TXT    | 100 MB                        |
| `sticker`  | Static or animated WebP             | 100 KB / 500 KB               |

### Location, contacts, reaction

| Field             | Content                                                       |
| ----------------- | ------------------------------------------------------------- |
| `location`        | `{ latitude, longitude, name, address }`                      |
| `locationRequest` | `{ body }` — shows a "Send location" button                   |
| `contacts`        | An array of contact cards                                     |
| `reaction`        | `{ messageId, emoji }` — an empty string removes the reaction |

### Interactive messages

```json
{
  "connectionId": "cnx_...",
  "to": "+237699001122",
  "buttons": {
    "body": "How can we help?",
    "footer": "Genuka",
    "buttons": [
      { "id": "order", "title": "My order" },
      { "id": "support", "title": "A problem" }
    ]
  }
}
```

| Field     | Description                    | Limits                            |
| --------- | ------------------------------ | --------------------------------- |
| `buttons` | Quick-reply buttons            | **3 max**, 20-character titles    |
| `list`    | Sections and rows              | 10 sections, **10 rows in total** |
| `cta`     | A single button carrying a URL | `url` + `displayText`             |

The user's answer arrives by webhook, carrying the `id` of the button or row they picked.

## Templates

Outside the 24-hour window, only an approved template will be delivered.

```json
{
  "connectionId": "cnx_...",
  "to": "+237699001122",
  "template": {
    "name": "order_confirmation",
    "language": "en",
    "body": ["Awa", "ORD-1042"]
  }
}
```

> [!NOTE]
> A template send opens a billable Meta conversation on the client's own WABA — Meta bills the
> client directly. Service messages sent inside the 24-hour window are free.

Create and track templates through `/api/v1/templates`. A template in `PAUSED` or `REJECTED`
status cannot be used: check its status before launching a campaign.

## Escape hatch

If a message type is not exposed yet, `raw` forwards a Cloud API fragment verbatim:

```json
{
  "connectionId": "cnx_...",
  "to": "+237699001122",
  "raw": { "type": "interactive", "interactive": { "type": "flow" } }
}
```

No validation is applied to `raw` — errors come back from Meta as-is.

## Common errors

| Meta code | Meaning                                           | What to do                      |
| --------- | ------------------------------------------------- | ------------------------------- |
| `131047`  | More than 24h since the last inbound message      | Send a template                 |
| `131026`  | Recipient unreachable or not on WhatsApp          | Do not retry                    |
| `131050`  | User opted out of marketing                       | Never send them marketing again |
| `131049`  | Per-user marketing cap reached                    | Retry later                     |
| `132001`  | Template missing or not approved in that language | Check name and language         |
| `130429`  | Throughput exceeded                               | Slow down, retry with backoff   |
