Skip to content

Create a template

Create a reusable email body you can later publish and send by id. See the Templates guide for the concept and the dashboard editor.

http
POST /v1/templates

Requires a Full access API key — templates are account content, not sending, so a sending key embedded in an app cannot create them.

A template is addressable by its id or its alias (the slug) everywhere a {id} appears in these endpoints. This one returns the id you'll use.

Body parameters

ParameterTypeRequiredDescription
namestringYesA human name for the template. Max 120 characters.
htmlstringYesThe HTML body. Every {{{tag}}} in it becomes a variable.
aliasstringNoThe slug you can address the template by instead of its id. Auto-derived from name if omitted, and deduplicated within your team (a colliding slug gains a -2, -3, … suffix). Max 64 characters.
fromstringNoA default sender — email@domain or Name <email@domain>. Used at send unless the send payload overrides it. Max 320 characters.
subjectstringNoA default subject line. Rendered with the same variables. Max 998 characters.
reply_tostringNoA default Reply-To address, overridable at send. Max 320 characters.
textstringNoA plain-text body. If omitted, plain text is derived from the HTML at send.
variablesarrayNoPer-variable flags — which are required, and their fallbacks. See Variables.

Example

bash
curl -X POST 'https://api.sendgrail.com/v1/templates' \
  -H "Authorization: Bearer $SENDGRAIL_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Welcome email",
    "alias": "welcome-email",
    "from": "Acme <hello@example.com>",
    "subject": "Welcome, {{{first_name}}}",
    "html": "<p>Hi {{{first_name}}}, your total is {{{price}}}.</p>",
    "variables": [
      { "key": "first_name", "required": true },
      { "key": "price", "fallback_value": "25" }
    ]
  }'
js
const res = await fetch('https://api.sendgrail.com/v1/templates', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SENDGRAIL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Welcome email',
    alias: 'welcome-email',
    from: 'Acme <hello@example.com>',
    subject: 'Welcome, {{{first_name}}}',
    html: '<p>Hi {{{first_name}}}, your total is {{{price}}}.</p>',
    variables: [
      { key: 'first_name', required: true },
      { key: 'price', fallback_value: '25' },
    ],
  }),
});

const template = await res.json();

Response

201 Created. Just the id — fetch the full object with Get a template when you want it.

json
{
  "object": "template",
  "id": "d3f8c2a1-6b47-4e9a-b25c-1f0e7a9d4c83"
}

A new template starts as a draft. It cannot be sent until you publish it — a send call referencing a draft is rejected.

Variables

Variables are discovered from the body, not declared. Every unique {{{tag}}} you write in the subject or the html/text becomes a variable automatically — you never list them to bring them into being.

The variables array only sets, per variable, two things:

FieldTypeDescription
keystringThe tag name, without the braces — e.g. price for {{{price}}}.
requiredbooleanWhether the value must be supplied at send. Defaults to false.
fallback_valuestringThe default rendered when the caller omits the value at send. Providing one makes the variable optional.

How they combine:

  • A variable with a fallback_value is optional, and renders that default when omitted. Give price a fallback_value of "25" and a send that omits it renders Total: 25.
  • A variable with no fallback and required: true must be supplied at send, or the send is rejected with 422.
  • A variable that is neither required nor given a fallback renders empty when omitted.

Naming a key that isn't in the body does nothing — only tags actually present become variables. And you don't have to list every tag: any you leave out are still discovered, just with required: false and no fallback.

Different from Resend

Resend declares each variable up front with a type (string or number). SendGrail discovers variables from the body instead and does not track a type — a variable here carries only its key, whether it's required, and an optional fallback_value.

Errors

A Sending access key gets 403 — creating templates needs Full access. See Errors.

Transactional email on your own domain.