Skip to main content

Notifications API

Customer emails are built from templates and sent by automation rules. A template holds the subject and body for one email type in one language. A rule says when to send which template, for example "24 hours before the booking starts".

All endpoints need a staff token with the admin:write permission and the x-tenant-id header. For the admin UI, see Emails and automations.

Templates​

MethodPathDescription
GET/api/v1/admin/messaging/templatesList templates.
POST/api/v1/admin/messaging/templatesCreate a template.
GET/api/v1/admin/messaging/templates/{id}Get a template.
PUT/api/v1/admin/messaging/templates/{id}Update a template.
DELETE/api/v1/admin/messaging/templates/{id}Delete a template. Returns 204.
POST/api/v1/admin/messaging/templates/{id}/cloneCopy a template, for example to another language.
GET/api/v1/admin/messaging/templates/key/{templateKey}The tenant-wide or default template for a key and language.
GET/api/v1/admin/messaging/templates/typesThe email types you can write templates for.
GET/api/v1/admin/messaging/templates/variables/{templateKey}The variables available for an email type.
GET/api/v1/admin/messaging/templates/variables/{templateKey}/categorizedThe same variables, grouped by category.
POST/api/v1/admin/messaging/templates/previewRender a subject and body with sample data.

List templates​

Query parameters, all optional: templateKey, languageCode, templateType (email or sms), siteId, and includeGlobal (default true, includes the built-in defaults).

[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenantId": "t_demo",
"siteId": null,
"templateKey": "booking_confirmed",
"languageCode": "en",
"displayName": "Booking confirmation",
"subjectTemplate": "Your booking {{bookingReference}} is confirmed",
"bodyTemplate": "<p>Hi {{customerName}}, ...</p>",
"templateType": "email",
"isGlobal": false,
"createdAt": "2026-09-01T10:30:00Z",
"updatedAt": "2026-09-01T10:30:00Z"
}
]

Built-in defaults have isGlobal: true and tenantId: null. You cannot change them; create your own template with the same key and language instead.

Which template is used​

GET /api/v1/admin/messaging/templates/key/{templateKey} shows the tenant-wide template for a key and languageCode (default en). It falls back to the built-in default, and then to English. It does not consider site-specific templates.

Create a template​

{
"templateKey": "booking_confirmed",
"languageCode": "fi",
"subjectTemplate": "Varausvahvistus {{bookingReference}}",
"bodyTemplate": "<p>Hei {{customerName}}, varauksesi on vahvistettu.</p>",
"templateType": "email",
"siteId": "s_demo"
}
FieldRequiredDescription
templateKeyYesThe email type. See GET .../templates/types.
subjectTemplateYesSubject line.
bodyTemplateYesHTML body.
plainTextTemplateNoPlain-text body.
languageCodeNoDefault en.
templateTypeNoemail (default) or sms.
siteIdNoUse only for this site. Leave it out for all your sites.
displayNameNoName shown in the admin UI.
descriptionNoNote shown in the admin UI.

A second template with the same key and language returns 409. A template with unbalanced blocks returns 400.

PUT accepts subjectTemplate, bodyTemplate, plainTextTemplate, displayName and description. clone takes an optional languageCode and displayName.

Email types​

GET .../templates/types returns key, name, description and triggerEvent for each type. The keys are:

KeySent when
booking_confirmedA booking is confirmed.
booking_reminderA set time before the booking starts.
booking_canceledA booking is cancelled.
booking_rescheduledA booking's date or time changes.
status_changedA booking's status changes.
booking_no_show_followupA customer is marked as a no-show.
course_startAround a course's first session.
course_sessionAround each course session.
course_endAround a course's last session.

Template syntax​

Templates use a Handlebars-style syntax. Use the variables endpoint to see exactly which variables an email type has.

SyntaxResult
{{customerName}}The value, HTML-escaped.
{{{bodyHtml}}}The value, not escaped.
{{#if siteAddress}}...{{else}}...{{/if}}Show a block when the value is set.
{{#unless isPaid}}...{{/unless}}Show a block when the value is not set.
{{#each items}}{{this}}{{/each}}Repeat a block for each item. {{@index}} is the position.
{{formatDate bookingDateTime}}The date, in the site's time zone and the email's language.
{{formatTime bookingDateTime}}The time, 24-hour clock.
{{formatCurrency totalAmount}}The amount with the currency symbol.
{{uppercase name}}, {{lowercase name}}Change the case.

Preview​

POST /api/v1/admin/messaging/templates/preview
{
"subjectTemplate": "Your booking {{bookingReference}}",
"bodyTemplate": "<p>Hi {{customerName}}</p>",
"sampleData": {
"customerName": "Alex Example",
"bookingReference": "BK-2026-001"
}
}

Returns { "subject": "...", "body": "...", "plainText": "..." }.

Automation rules​

MethodPathDescription
GET/api/v1/admin/messaging/automation-rulesList rules.
POST/api/v1/admin/messaging/automation-rulesCreate a rule.
GET/api/v1/admin/messaging/automation-rules/{id}Get a rule.
PUT/api/v1/admin/messaging/automation-rules/{id}Update a rule. Send only the fields you change.
DELETE/api/v1/admin/messaging/automation-rules/{id}Delete a rule. Returns 204.

The list takes optional triggerEvent, isActive and templateKey filters.

Create a rule​

{
"name": "24-hour reminder",
"triggerEvent": "booking_date",
"triggerOffset": -24,
"triggerOffsetUnit": "hours",
"templateKey": "booking_reminder",
"productIds": [],
"productTags": [],
"siteIds": [],
"isActive": true
}
FieldRequiredDescription
nameYesRule name.
triggerEventYesWhat the rule reacts to. See below.
triggerOffsetYesTime from the event. Negative is before, positive is after, 0 is at once.
templateKeyYesThe email type to send.
triggerOffsetUnitNohours or days.
triggerSessionNumberNoFor course_session only: the session to target, starting at 1. Leave it out for every session.
productIdsNoOnly these products. Empty means all.
productTagsNoOnly products with any of these tags. Empty means all.
siteIdsNoOnly these sites. Empty means all.
destinationTypeNoemail (default) or webhook.
integrationIdNoThe webhook integration, when destinationType is webhook.
descriptionNoNote for staff.
isActiveNoWhether the rule runs.
triggerEventOffset is measured from
booking_createdWhen the booking is created.
booking_confirmedWhen the booking is confirmed.
booking_completedWhen the booking is marked complete.
booking_dateThe booking's start time.
course_startThe course's first session.
course_sessionEach course session.
course_endThe course's last session.

Responses include isSystem (a default rule created for every account) and isProtected. A protected rule sends a required confirmation, cancellation or reschedule email; you can edit it but not deactivate it (409).

Errors​

StatusWhen
400Invalid body or template syntax.
404Template, rule or email type not found.
409Duplicate template for a key and language, or deactivating a protected rule.

See Authentication for tokens.