> For the complete documentation index, see [llms.txt](https://developers.textcus.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.textcus.com/overview/whatsapp.md).

# Whatsapp

## TextCus WhatsApp API (v2)

Send WhatsApp messages from your own systems. Authentication, response shape and error handling match the TextCus SMS API, so if you have already integrated that, this will feel familiar.

**Base URL:** `https://api.textcus.com/api/v2`

***

### Authentication

Send your API key as a Bearer token on every request. You'll find it in your dashboard under Settings.

```
Authorization: Bearer YOUR_API_KEY
```

A missing or unknown key returns `401`.

### Response shape

Every response uses the same envelope:

```json
{ "status": true, "message": "1 message(s) sent successfully", "data": { } }
```

`status` is `false` for any failure, and `message` is safe to show to your own users.

***

### Before you can send

WhatsApp is not SMS — you cannot send arbitrary text to someone who has not messaged you first. Two rules govern everything:

1. **To start a conversation you must use a template Meta has approved.** Create templates in your TextCus dashboard; approval usually takes minutes.
2. **Once a customer replies, you have 24 hours** in which you can send free-form text. After that window closes you need a template again.

Sending is charged per message against your **WhatsApp balance**, which is separate from your SMS balance and held in **GHS**. Prices depend on the template's category and drop as your monthly volume grows — call `/whatsapp/balance` to see your current rates.

***

### POST /whatsapp/send

```bash
curl -X POST https://api.textcus.com/api/v2/whatsapp/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sender": "233241234567",
    "recipient": "0244000001",
    "template": "order_shipped",
    "language": "en_US",
    "variables": { "body": ["Ama", "ORD-1042"] }
  }'
```

| Field        | Required           | Notes                                                                                                 |
| ------------ | ------------------ | ----------------------------------------------------------------------------------------------------- |
| `sender`     | yes                | One of your connected WhatsApp numbers. `233241234567`, `+233 24 123 4567` and `0241234567` all work. |
| `recipient`  | one of             | A single destination number.                                                                          |
| `recipients` | one of             | Up to 1000 numbers. Duplicates are removed.                                                           |
| `template`   | yes\*              | Template name. \*Not needed when sending free-form text.                                              |
| `language`   | no                 | Defaults to `en_US`. Must match the template's language exactly.                                      |
| `variables`  | no                 | `{"header": [...], "body": [...]}` — plain lists, in order.                                           |
| `type`       | no                 | `template` (default) or `text`.                                                                       |
| `message`    | yes if `type=text` | Free-form body. Only valid inside an open 24-hour window.                                             |

Local numbers are converted to international format automatically, so `0244000001` becomes `233244000001`.

#### Response

```json
{
  "status": true,
  "message": "1 message(s) sent successfully",
  "data": {
    "sent": 1,
    "failed": 0,
    "messages": [
      { "recipient": "233244000001", "message_id": "wamid.HBgMMjMz...", "status": "sent" }
    ],
    "failures": [],
    "unit_price": 0.1003,
    "charged": 0.1003,
    "balance": 42.6,
    "currency": "GHS"
  }
}
```

Keep `message_id` — it is how you look a message up later, and it is the same id Meta uses.

#### Partial success

A bulk send is not all-or-nothing. Each recipient is charged and attempted independently, and anything Meta rejects is **refunded immediately**:

```json
{
  "sent": 2,
  "failed": 1,
  "failures": [
    { "recipient": "233209999999", "error": "Recipient not on WhatsApp" }
  ]
}
```

You are never charged for a message Meta refused. Messages that fail later during delivery are refunded when Meta reports the failure, and show `"refunded": true` on the status endpoint.

***

### GET /whatsapp/status/{message\_id}

```bash
curl https://api.textcus.com/api/v2/whatsapp/status/wamid.HBgMMjMz... \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```json
{
  "status": true,
  "data": {
    "message_id": "wamid.HBgMMjMz...",
    "recipient": "233244000001",
    "status": "delivered",
    "charge": 0.1003,
    "refunded": false,
    "sent_at": "2026-09-05T14:12:03+00:00",
    "delivered_at": "2026-09-05T14:12:09+00:00",
    "read_at": null
  }
}
```

Statuses progress `queued` → `sent` → `delivered` → `read`, or end at `failed`. Inbound messages are recorded as `received`.

***

### GET /whatsapp/messages

Paginated history. Optional filters: `status`, `direction` (`outbound` / `inbound`), `recipient`, `per_page` (max 200).

```bash
curl "https://api.textcus.com/api/v2/whatsapp/messages?status=failed&per_page=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Replies from your customers appear here with `"direction": "inbound"` and no charge.

***

### GET /whatsapp/senders

```json
{
  "data": [
    {
      "sender": "+233 24 123 4567",
      "name": "Acme Ghana",
      "quality_rating": "GREEN",
      "verified": true,
      "active": true,
      "connected_at": "2026-09-01T09:14:22+00:00"
    }
  ]
}
```

`quality_rating` is Meta's health score for the number. `GREEN` is healthy; `RED` means your messaging limit is at risk.

***

### GET /whatsapp/templates

Approved templates only by default. Pass `?include_unapproved=true` to see pending and rejected ones too.

```json
{
  "data": [
    {
      "name": "order_shipped",
      "language": "en_US",
      "category": "UTILITY",
      "status": "APPROVED",
      "body": "Hi {{1}}, order {{2}} shipped.",
      "variables": { "header": 0, "body": 2 }
    }
  ]
}
```

`variables` tells you how many values each section expects — `body: 2` means send `"variables": {"body": ["...", "..."]}` in that order.

***

### GET /whatsapp/balance

```json
{
  "data": {
    "whatsapp_balance": 42.6,
    "currency": "GHS",
    "tier": "Starter",
    "monthly_volume": 1204,
    "rates": {
      "marketing": 0.38,
      "utility": 0.1003,
      "authentication": 0.1003,
      "service": 0
    }
  }
}
```

Rates shown are **your** current prices at your current volume tier, per message, in GHS.

***

### Errors

| Code  | Meaning                                     | What to do                                                   |
| ----- | ------------------------------------------- | ------------------------------------------------------------ |
| `401` | Missing or invalid API key                  | Check the `Authorization` header.                            |
| `402` | Insufficient WhatsApp balance               | Response includes `required` and `balance`. Top up.          |
| `404` | Sender, template or message not found       | Check the name; templates are per sender *and* per language. |
| `422` | Validation failed, or template not approved | `message` says exactly what is wrong.                        |
| `429` | Rate limited                                | 60 requests per minute. Back off and retry.                  |
| `500` | Something broke on our side                 | Safe to retry. Contact support if it persists.               |

Nothing is charged on a `4xx`.

***

### Notes for production

* **Rate limit is 60 requests/minute.** Prefer one call with `recipients` over many calls with `recipient` — a single request can carry 1000 numbers.
* **Retries are not deduplicated.** If a request times out, check `/whatsapp/messages?recipient=...` before resending, or you may pay twice.
* **Get consent before messaging.** WhatsApp requires opt-in, and blocks or "report" taps push your quality rating down fast. A `RED` rating cuts how many people you can reach per day.
