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

# Email templates

> Create text and HTML templates and send personalized messages to one or many destinations.

Templates store a subject and body with variables. Names are immutable and
unique within the account. `body_type` is `text` or `html`.

## Create a template

<Tabs>
  <Tab title="Console">
    Open Email, then **Templates**, and click **Create template**. Enter the
    subject and body templates and choose the body type. Open the saved template
    to edit it or send a test message.
  </Tab>

  <Tab title="API">
    Send `POST /v1/templates`:

    ```json theme={null}
    {
      "name":"report-ready",
      "subject_template":"Your report, {{name}}",
      "body_template":"<p>Hello {{name}}, your report is ready.</p>",
      "body_type":"html"
    }
    ```

    Use `PATCH /v1/templates/{template_id}` to update content. The template name
    remains unchanged. Omitted subject, body, and type are preserved. An empty
    `subject_template` clears the subject; the body must remain nonempty.
  </Tab>

  <Tab title="CLI">
    Save the JSON request from the API tab as `request-1.json`. Use your configured account and region.

    ```bash theme={null}
    basaltic email template create --from-file request-1.json
    ```
  </Tab>

  <Tab title="Go">
    Save the JSON request from the API tab as `request-1.json`. Configure credentials, account, and region through the SDK environment variables.

    ```go theme={null}
    package main

    import (
    	"context"
    	"encoding/json"
    	"fmt"
    	"os"

    	basaltic "github.com/basaltic-sh/sdk-go"
    	"github.com/basaltic-sh/sdk-go/email"
    )

    func readJSON(path string, value any) error {
    	data, err := os.ReadFile(path)
    	if err != nil {
    		return err
    	}
    	return json.Unmarshal(data, value)
    }

    func run() error {
    	ctx := context.Background()
    	cfg, err := basaltic.NewConfig(ctx)
    	if err != nil {
    		return err
    	}
    	client := email.New(cfg)
    	var request1 email.CreateTemplateRequest
    	if err := readJSON("request-1.json", &request1); err != nil {
    		return err
    	}
    	result1, err := client.CreateTemplate(ctx, &request1)
    	if err != nil {
    		return err
    	}
    	fmt.Printf("%+v\n", result1)
    	return nil
    }

    func main() {
    	if err := run(); err != nil {
    		fmt.Fprintln(os.Stderr, err)
    		os.Exit(1)
    	}
    }
    ```
  </Tab>
</Tabs>

## Variables and escaping

Use `{{name}}` or `{{ name }}` for a simple variable. `template_data` is an object
whose values are strings. The response's `variables` field lists detected
variables. Missing values render as empty strings, so validate required values
in your application before sending. HTML bodies escape inserted variable values;
subjects and text bodies are plain text.

Send `POST /v1/sent-messages` with template content:

```json theme={null}
{
  "identity": "example.com",
  "from": "updates@example.com",
  "to": ["reader@example.net"],
  "content": {
    "template": "report-ready",
    "template_data": {"name": "Alex"}
  }
}
```

The identity and template accept UUIDs, names, or CRNs. Sending requires
`email:SendMessage` on the identity and `email:SendTemplatedMessage` on the template.
`configuration_set` can apply tracking, pool selection, and suppression overrides.
The template is rendered before queueing; later template edits do not change
accepted messages.

## Batch sends

`POST /v1/sent-messages/batch` accepts a `messages` array with up to 50 ordinary
send requests. Each entry can use text/HTML, raw MIME, or template content.
Supply personalized `template_data` in each entry.

Identity and template permissions are checked for every entry before any message
is queued. Sending restrictions, rendering, suppression, storage, and quota failures
then produce individual results. Results preserve input order; `index` identifies
the input and `status` is `queued` or `rejected`. Queued results include a `message`
ResourceRef. Rejected results include the standard `error` object with `code`,
`message`, and `request_id`. Queued does not mean delivered.

Check every result even when the HTTP request succeeds. Preserve the original
Idempotency-Key for transport retries of the same batch. Retry only rejected
entries as a new request after correcting their errors; a new key for the entire
batch can duplicate messages already accepted.


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