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

# SMS templates

> Reusable message bodies with server-rendered variables.

Templates let you define a message body once, with `{{.variable}}` placeholders, and render it server-side on every send. Benefits: shorter requests, consistent copy, and a single place to edit when the copy changes.

## Create a template

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.robase.dev/v1/sms-templates \
    -H "Authorization: Bearer rb_live_xxx" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Ride arriving",
      "body": "Hi {{.driver_name}}, your ride arrives in {{.eta}} min."
    }'
  ```

  ```typescript Node.js theme={null}
  await pm.smsTemplates.create({
    name: 'Ride arriving',
    body: 'Hi {{.driver_name}}, your ride arrives in {{.eta}} min.',
  });
  ```

  ```python Python theme={null}
  pm.sms_templates.create(
      name="Ride arriving",
      body="Hi {{.driver_name}}, your ride arrives in {{.eta}} min.",
  )
  ```

  ```go Go theme={null}
  pm.SMSTemplates.Create(ctx, &robase.UpsertSMSTemplateRequest{
      Name: "Ride arriving",
      Body: "Hi {{.driver_name}}, your ride arrives in {{.eta}} min.",
  })
  ```
</CodeGroup>

Response:

```json theme={null}
{
  "id": "tpl_01H8XKQJ3Z...",
  "name": "Ride arriving",
  "slug": "ride-arriving",
  "body": "Hi {{.driver_name}}, your ride arrives in {{.eta}} min.",
  "variables": ["driver_name", "eta"]
}
```

The `slug` is derived from `name` and doubles as a stable identifier — use it (instead of the UUID) in sends so renaming a template doesn't break callers.

## Send using the template

```typescript theme={null}
await pm.sms.send({
  to:   '+2348012345678',
  from: 'SHUTTLERS',
  template: 'ride-arriving',             // slug or UUID both work
  variables: { driver_name: 'Chidi', eta: 3 },
});
```

The server renders the body, re-computes segments and cost, and proceeds normally.

## Variable syntax

We use Go's `text/template` engine:

| Syntax                            | Renders                        |
| --------------------------------- | ------------------------------ |
| `{{.name}}`                       | The value of `variables.name`  |
| `{{.user.email}}`                 | Nested lookup — requires a map |
| `{{printf "%d" .count}}`          | Formatters via pipe            |
| `{{if .is_first}}Welcome!{{end}}` | Conditionals                   |

<Tip>
  Missing variables render as `<no value>` — not an error. This is deliberate so a typo doesn't block a send; but test your templates with the render endpoint (below) to catch them in dev.
</Tip>

## Preview before sending

`POST /v1/sms-templates/:id/render` renders the template with variables and returns the body + segment count **without** sending anything. Great for previews in your admin UI:

```typescript theme={null}
const preview = await pm.smsTemplates.render('tpl_01H8...', {
  driver_name: 'Chidi',
  eta: 3,
});
// → { body: "Hi Chidi, your ride arrives in 3 min.", encoding: "GSM7", segments: 1 }
```

## List, update, delete

```typescript theme={null}
await pm.smsTemplates.list();
await pm.smsTemplates.get('tpl_01H8...');
await pm.smsTemplates.update('tpl_01H8...', { body: 'Updated: ...' });
await pm.smsTemplates.remove('tpl_01H8...');
```

## Keeping segment count predictable

Variables change the final length. A good pattern: cap user-supplied variables to a max length before passing them, so your template never balloons past one segment:

```typescript theme={null}
const eta = Math.min(Number(rawEta), 99);  // cap at 99 → 2 chars max
const driver = String(driverName).slice(0, 20);

await pm.sms.send({
  to, from, template: 'ride-arriving',
  variables: { eta, driver_name: driver },
});
```

The render endpoint is useful here too — check `segments` before batch-sending to estimate cost.
