Templates

Reusable content for campaigns, transactional sends and automations.

GET/POST/api/v1/templatesElenca o crea un template di contenuto riusabile (e' cosi' che si ottiene un templateId valido).(scope: templates:read|write)
GET/PATCH/api/v1/templates/:idDettaglio col contenuto, oppure modifica parziale (i campi assenti restano).(scope: templates:read|write)
DELETE/api/v1/templates/:idElimina un template: 409 se una campagna lo usa ancora.(scope: templates:write)

A template is reusable content stored in your workspace. Every send path accepts a templateId instead of inline content: POST /api/v1/emails, POST /api/v1/messages, campaigns, and the message nodes of an automation. This is the endpoint that gives you a valid one.

POST /api/v1/templates

FieldTypeRequiredNotes
namestringyes2 to 200 chars. Unique per workspace.
bodystringyesThe content. With format: "blocks" it is the JSON of an EmailDoc.
channelsarraynoAny of email, whatsapp, telegram. Defaults to ["email"].
formatstringnoEmail: markdown, html (default) or blocks. Chat channels are always text.
subjectstringyes for emailRejected with subject_required when the template covers email.
channelTextobjectnoPer-channel text for whatsapp / telegram. Without it the base body is degraded to plain text.
const { template } = await sending.templates.create({
  name: "Welcome",
  subject: "Welcome aboard, {{firstName}}",
  format: "html",
  body: "<p>Hi {{firstName}}, glad you are here.</p>",
});
 
await sending.emails.send({
  from: "Acme <[email protected]>",
  to: "[email protected]",
  templateId: template.id,
  idempotencyKey: "welcome-sara-001",
});

The variables array in the response is derived from the content: the {{...}} placeholders are read from subject, body and per-channel overrides, so you never declare them. A placeholder that matches no contact field comes back in warnings, and is not an error: at send time it renders empty (or falls back to {{key|default:...}}).

Names are unique

A duplicate name answers 409 template_exists and includes the templateId that already exists. It does not attach to it the way tags and lists do: those are records, where the natural key is the thing, while a template is content, and handing back the id of different content would send a campaign with the wrong body. Reuse the id you get back, or fix it with PATCH.

GET /api/v1/templates

Returns up to 200 templates, most recently updated first, without their body. Add ?channel=email to keep only the ones usable on a channel, ?limit= to page down.

GET /api/v1/templates/{id}

The full template, body included. Read it before correcting it, and to see which variables it expects before using it in a send.

PATCH /api/v1/templates/{id}

Partial update: only the fields you pass change. The rules stay coupled to each other, so changing just the body of a blocks template still requires a valid EmailDoc, and removing the subject of an email template is rejected.

Campaigns that already went out do not change: they carry the content frozen at send time.

curl -X PATCH "https://sending.dev/api/v1/templates/<id>" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{ "subject": "Welcome aboard!" }'

DELETE /api/v1/templates/{id}

Rejected with 409 template_in_use while a campaign still references the template; the response lists the campaigns, so you know what to fix first.

Errors

CodeStatusMeaning
template_exists409A template with that name exists. The response carries its templateId.
template_in_use409A campaign still references it. The response lists them.
subject_required422The template covers email and has no subject.
invalid_format422Email templates accept markdown, html or blocks only.
invalid_document422format: "blocks" and the body is not a valid EmailDoc.
no_channels422No valid channel left after normalization.

MCP

The same surface is available to agents as list_templates, get_template, create_template and update_template, under the templates:read and templates:write scopes.