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

# Send a WhatsApp template

> Find an approved template, fill its variables and buttons, and send it through the API or the PHP SDK.

Templates are the only messages WhatsApp accepts outside the 24-hour window. They must be approved by Meta first: create them in the dashboard, through the API or with a hosted link, see [Create WhatsApp templates](https://docs.shortext.ny-corp.io/api-reference/guides/create-templates.md).

> [!TIP]
>
> In the dashboard, the **Use** action of a template (**Communication > WhatsApp templates**) gives the ready-to-copy code that sends it, filled with its examples: API v2.0, PHP SDK and API v1.0.

## 1. Find the template

`GET /templates?channel_id={whatsapp channel id}&status=APPROVED` lists the templates a number can send (key with **Send messages**):

```json theme={null}
{
  "id": "t4Hd6sFe",
  "name": "order_update",
  "language": "fr",
  "status": "APPROVED",
  "category": "UTILITY",
  "channels": ["532867649916076"],
  "header": { "type": "NONE", "text": null, "variables": [], "media_url": null },
  "body": { "text": "Bonjour {{name}}, votre commande {{order}} est prête.", "variables": ["name", "order"] },
  "footer": null,
  "buttons": [
    { "index": 0, "type": "URL", "text": "Track my order", "needs_value": true },
    { "index": 1, "type": "QUICK_REPLY", "text": "Contact us", "needs_value": false }
  ]
}
```

* `body.variables` (and `header.variables` for a text header) are the values you must give.
* `buttons` lists the buttons in order, with their `index`. A button with `needs_value: true` needs a value when you send the template: the end of a dynamic link, the coupon of a copy-code button, or the code of an authentication template.

## 2. Send it

Use `message.type` `template`, the template `name` and `language`, the variables by name, and the button values by index:

```bash cURL theme={null}
curl -X POST https://shortext.ny-corp.io/api/v2.0/conversations \
  -H "Authorization: Bearer $SHORTEXT_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "whatsapp",
    "channel_id": "532867649916076",
    "contact": {
      "identifier": "237699999999",
      "profile": { "firstname": "Brice", "lastname": "Mbarga", "preferred_language": "fr" }
    },
    "message": {
      "type": "template",
      "content": {
        "template_name": "order_update",
        "language": "fr",
        "body": { "name": "Brice", "order": "A-1042" }
      }
    }
  }'
```

```php PHP theme={null}
<?php
$response = (new GuzzleHttp\Client())->post('https://shortext.ny-corp.io/api/v2.0/conversations', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('SHORTEXT_API_KEY'),
        'Accept'        => 'application/json',
    ],
    'json' => [
        'channel'    => 'whatsapp',
        'channel_id' => '532867649916076',
        'contact'    => [
            'identifier' => '237699999999',
            'profile'    => ['firstname' => 'Brice', 'lastname' => 'Mbarga', 'preferred_language' => 'fr'],
        ],
        'message'    => [
            'type'    => 'template',
            'content' => [
                'template_name' => 'order_update',
                'language'      => 'fr',
                'body'          => ['name' => 'Brice', 'order' => 'A-1042'],
            ],
        ],
    ],
]);

$result = json_decode((string) $response->getBody(), true);
if (!$result['success']) {
    throw new RuntimeException("Shortext error {$result['code']}: {$result['message']}");
}
$conversationId = $result['body']['conversation']['id'];
```

```javascript Node.js theme={null}
const response = await fetch("https://shortext.ny-corp.io/api/v2.0/conversations", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SHORTEXT_API_KEY}`,
    Accept: "application/json",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    channel: "whatsapp",
    channel_id: "532867649916076",
    contact: {
      identifier: "237699999999",
      profile: { firstname: "Brice", lastname: "Mbarga", preferred_language: "fr" },
    },
    message: {
      type: "template",
      content: {
        template_name: "order_update",
        language: "fr",
        body: { name: "Brice", order: "A-1042" },
      },
    },
  }),
});

const result = await response.json();
if (!result.success) {
  throw new Error(`Shortext error ${result.code}: ${result.message}`);
}
const conversationId = result.body.conversation.id;
```

```python Python theme={null}
import os
import requests

response = requests.post(
    "https://shortext.ny-corp.io/api/v2.0/conversations",
    headers={
        "Authorization": f"Bearer {os.environ['SHORTEXT_API_KEY']}",
        "Accept": "application/json",
    },
    json={
        "channel": "whatsapp",
        "channel_id": "532867649916076",
        "contact": {
            "identifier": "237699999999",
            "profile": {"firstname": "Brice", "lastname": "Mbarga", "preferred_language": "fr"},
        },
        "message": {
            "type": "template",
            "content": {
                "template_name": "order_update",
                "language": "fr",
                "body": {"name": "Brice", "order": "A-1042"},
            },
        },
    },
)

result = response.json()
if not result["success"]:
    raise RuntimeError(f"Shortext error {result['code']}: {result['message']}")
conversation_id = result["body"]["conversation"]["id"]
```

To send a template in an existing conversation, post the same `message` to `POST /conversations/{id}/messages`.

### Buttons

`content.buttons` gives the value of each button by its index (from 0):

```json theme={null}
"buttons": { "0": "A-1042", "1": "SAVE20" }
```

| Button | Value |
| - | - |
| Dynamic link (`URL` with a variable) | The end of the link: for `https://shop.example/orders/{{1}}`, send `A-1042`. Required. |
| Copy code (`COPY_CODE`) | The coupon code. Required. |
| Quick reply (`QUICK_REPLY`) | Optional: the payload you get back when the contact taps it. |

Static links and call buttons take no value.

### Media header

For a template with an image, video or document header, give its URL:

```json theme={null}
"header": { "url": "https://cdn.example.com/parcels/A-1042.jpg" }
```

Without `url`, the media stored with the template is used. A document also takes a `filename`.

### Authentication templates

An authentication template carries a one-time code. Send it in `content.code`: Shortext puts it in the body and in the code button.

```json theme={null}
{
  "type": "template",
  "content": { "template_name": "login_code", "language": "en", "code": "482913" }
}
```

### Billing category

You are billed for the category Meta approved for the template (`UTILITY`, `MARKETING` or `AUTHENTICATION`), whatever you send.

## With the PHP SDK

The PHP SDK (`composer require nycorp/shortext-php`, version 2.1 or later) builds the same message:

```php theme={null}
use NyCorp\Shortext\Messages\Contact;
use NyCorp\Shortext\Messages\Message;
use NyCorp\Shortext\Shortext;

$shortext = new Shortext(getenv('SHORTEXT_API_KEY'));

$result = $shortext->conversations()->start(
    channel: 'whatsapp',
    channelId: '532867649916076',
    contact: Contact::make('237699999999')->lastname('Mbarga'),
    message: Message::template('order_update', 'fr')
        ->body(['name' => 'Brice', 'order' => 'A-1042'])
        ->buttonValues([0 => 'A-1042']),
);
```

For an authentication template: `Message::template('login_code', 'en')->code('482913')`.

## Errors

Shortext checks the template before sending it and answers `code` `1002` with a message you can act on:

| Message | Fix |
| - | - |
| `Template order_update not found for this WhatsApp number` | Check the name, and that the template belongs to the WhatsApp Business account of this number. |
| `Template order_update has no en version: fr` | Use one of the languages listed. |
| `Template order_update (fr) is PENDING: only approved templates can be sent` | Wait for Meta's approval (`template.status_updated` webhook). |
| `Missing body variables: order` | Give every variable; `body.missing` lists them. |
| `Template ... has a image header: give message.content.header.url` | Give the media URL. |
| `Button 0 needs the end of its link: give it in content.buttons` | Give the value of every button with `needs_value: true`. |
| `The template has no button 2 (buttons are numbered from 0)` | Use the indexes listed by `GET /templates`. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.