# Templates

> Save a text once with variables in it, then send it by id from the API, MCP, broadcasts, sequences and keyword replies, filled in for each person.

A template is a text you save once and send many times, with `{{variables}}` where each person’s details go. Dana’s team sends the same showing reminder a dozen times a week, so she saved it:

```text
Hi {{first_name|there}}, see you at {{time}} at {{address}}.
```

Make templates on the **Templates** page or with `POST /v1/templates`. Each has a name, unique among your templates, and an id that starts `tpl_`.

## Variables

A variable is a name in double braces. When a template is sent, each one is filled from the first of these that has a value:

1. What the sender passed for this send: `variables` on `POST /v1/messages`, a column of a broadcast’s CSV, or a field a Text Hook received.
2. The person’s contact: `first_name`, `last_name`, `full_name`, `company`, `phone`, `email`, and any custom field by its name (`{{showing}}`, or `{{custom.showing}}` to be explicit).
3. `business`: your business name from **Settings** (`business_name` on `GET /v1/settings`). Until you set one it has no value, so use a fallback such as `{{business|us}}` or set the name first.

`{{name|fallback}}` gives the words to use when a variable has no value. `Hi {{first_name|there}}` reads “Hi Maria” for Maria and “Hi there” for somebody with no name on file.

A variable with no value and no fallback is left out, with the space before it when punctuation follows, so `Hi {{first_name}}, …` reads “Hi, …”. A broadcast, a sequence or a keyword reply sends it that way. A send through the API or MCP refuses instead, so your code finds out before anybody gets a text with a gap in it.

## Sending one from the API

Send `template_id` in place of `text`, and `variables` for anything the contact doesn’t already have:

```bash
curl -X POST "https://usebluebot.com/api/v1/messages" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: showing-48213-reminder" \
  -d '{
    "to": "+15555550123",
    "template_id": "tpl_1Hq2LxW9cVbN3mKpRtYs0a",
    "variables": {
      "time": "3 PM",
      "address": "412 Oak St"
    }
  }'
```

```ts
import BlueBot from '@usebluebot/sdk';

const client = new BlueBot({ apiKey: process.env.BLUEBOT_API_KEY });

const message = await client.messages.send({
  to: '+15555550123',
  templateId: 'tpl_1Hq2LxW9cVbN3mKpRtYs0a',
  variables: {
    time: '3 PM',
    address: '412 Oak St',
  },
}, { idempotencyKey: 'showing-48213-reminder' });
console.log(message);
```

```python
import os
from bluebot import BlueBot

client = BlueBot(api_key=os.environ["BLUEBOT_API_KEY"])

message = client.messages.send(
    to="+15555550123",
    template_id="tpl_1Hq2LxW9cVbN3mKpRtYs0a",
    variables={
        "time": "3 PM",
        "address": "412 Oak St",
    },
    idempotency_key="showing-48213-reminder",
)
print(message)
```

The text is filled in when BlueBot accepts it, so the answer carries the finished words, and `source_ref` is the template’s id. Changing the template afterwards doesn’t change texts already sent or scheduled. [Send a text](https://usebluebot.com/docs/sending/texts) has every field of a send.

| You got                                       | It means                                                                        | Do next                                                             |
| --------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `201`                                         | The template was filled in and the text accepted.                               | Read `text` in the answer to see the words that will go.            |
| `400 invalid_request` naming `variables.time` | The template needs `time`, and neither your `variables` nor the contact had it. | Send it in `variables`, or add a fallback such as `{{time\|soon}}`. |
| `400 invalid_request` naming `template_id`    | You sent `text` and `template_id` together.                                     | Send one of them.                                                   |
| `404 not_found` naming `template_id`          | No template of yours has that id, or it was deleted.                            | List yours with `GET /v1/templates`.                                |

## Where else templates are used

- **The inbox.** The composer’s template menu fills one in for the person you’re texting, and you edit it before sending.
- **Broadcasts, sequences and keyword replies** can each send a template in place of their own words, filled in for each person. Sequences and keyword replies read the template each time they send, so an edit changes their next text. A broadcast copies the template’s words when you make it.
- **MCP.** `send_template` fills one in for an AI assistant, with the same rules as the API. [MCP tools](https://usebluebot.com/docs/mcp/tools)

## Keeping them

A template is your own content, so BlueBot keeps it for as long as you do, unlike the words of a sent text, which go after 30 days. [Data retention](https://usebluebot.com/docs/concepts/data-retention)
