Skip to main content

Notification Templates

Templates let modules define reusable notification content once and send it with variable substitution on demand. A single template can target multiple channels simultaneously — one sendFromTemplate() call sends an email and an in-app WebSocket notification at the same time.


Create a Template

POST https://api.septemcore.com/v1/notifications/templates
Authorization: Bearer <access_token>
Content-Type: application/json

{
"name": "invoice-ready",
"description": "Sent when an invoice is generated for a customer",
"channels": [
{
"channel": "email",
"subject": "Invoice #{{invoiceNumber}} is ready",
"body": "Hi {{name}},\n\nYour invoice #{{invoiceNumber}} for {{amount}} is ready.\nDue date: {{dueDate}}.\n\nDownload: {{downloadUrl}}"
},
{
"channel": "websocket",
"body": "Invoice #{{invoiceNumber}} ready — {{amount}} due {{dueDate}}"
}
]
}

Response 201 Created:

{
"templateId": "01j9patpl900000000000000",
"name": "invoice-ready",
"channels": ["email", "websocket"],
"createdAt": "2026-04-15T10:30:00.000Z",
"version": 1
}

Variable Syntax

Template bodies use {{variableName}} placeholders. Variables are replaced at send time from the variables map passed to sendFromTemplate(). Unresolved variables (present in template but absent from the variables map) are replaced with an empty string and the notification is still sent — no error is returned.

Template body: "Hi {{name}}, your balance is {{balance}}."
Variables: { "name": "Alice" }
Rendered: "Hi Alice, your balance is ."

For required variables, validate their presence in your module code before calling sendFromTemplate().


SDK — sendFromTemplate()

import { kernel } from '@platform/sdk-core';

await kernel.notify().sendFromTemplate({
templateId: '01j9patpl900000000000000',
userId: '01j9pa5mz700000000000000',
variables: {
name: 'Alice Chen',
invoiceNumber: '1042',
amount: '$250.00',
dueDate: '2026-04-30',
downloadUrl: 'https://app.acme.com/invoices/1042',
},
priority: 'normal',
});

The SDK resolves which channels the template targets, renders each channel's body with the provided variables, and dispatches one notification per channel — all in a single call.


REST — sendFromTemplate()

POST https://api.septemcore.com/v1/notifications
Authorization: Bearer <access_token>
Content-Type: application/json

{
"templateId": "01j9patpl900000000000000",
"userId": "01j9pa5mz700000000000000",
"variables": {
"name": "Alice Chen",
"invoiceNumber": "1042",
"amount": "$250.00",
"dueDate": "2026-04-30",
"downloadUrl": "https://app.acme.com/invoices/1042"
},
"priority": "normal"
}

Response 202 Accepted:

{
"notifications": [
{ "notificationId": "01j9panot700000000000000", "channel": "email", "status": "queued" },
{ "notificationId": "01j9panot800000000000000", "channel": "websocket", "status": "queued" }
]
}

One notificationId is returned per channel in the template. Each can be tracked independently.


Multiplex: One Template, Multiple Channels

A template with two or more channels in its channels array delivers to all of them in a single sendFromTemplate() call:

Template "invoice-ready":
channels: ["email", "websocket"]

sendFromTemplate({ templateId: 'invoice-ready', userId: '...', variables: {...} })

├──▶ email: Renders email body → enqueues to RabbitMQ → SendGrid
└──▶ websocket: Renders WebSocket body → pushes to browser connection

Each channel delivery is independent: if the email adapter is down and retrying, the WebSocket notification is delivered immediately. One channel's failure never blocks another channel.


List Templates

GET https://api.septemcore.com/v1/notifications/templates
Authorization: Bearer <access_token>
{
"data": [
{
"templateId": "01j9patpl900000000000000",
"name": "invoice-ready",
"channels": ["email", "websocket"],
"version": 1,
"createdAt": "2026-04-15T10:30:00.000Z"
},
{
"templateId": "01j9patpl910000000000000",
"name": "welcome",
"channels": ["email"],
"version": 2,
"createdAt": "2026-04-10T08:00:00.000Z"
}
],
"pagination": {
"nextCursor": null,
"hasMore": false
}
}

Retrieve a Template

GET https://api.septemcore.com/v1/notifications/templates/01j9patpl900000000000000
Authorization: Bearer <access_token>
{
"templateId": "01j9patpl900000000000000",
"name": "invoice-ready",
"description": "Sent when an invoice is generated for a customer",
"version": 1,
"channels": [
{
"channel": "email",
"subject": "Invoice #{{invoiceNumber}} is ready",
"body": "Hi {{name}},\n\nYour invoice #{{invoiceNumber}} for {{amount}} is ready.\nDue date: {{dueDate}}."
},
{
"channel": "websocket",
"body": "Invoice #{{invoiceNumber}} ready — {{amount}} due {{dueDate}}"
}
],
"createdAt": "2026-04-15T10:30:00.000Z"
}

Update a Template

PUT https://api.septemcore.com/v1/notifications/templates/01j9patpl900000000000000
Authorization: Bearer <access_token>
Content-Type: application/json

{
"channels": [
{
"channel": "email",
"subject": "Invoice #{{invoiceNumber}} is ready — action required",
"body": "Hi {{name}},\n\nYour invoice #{{invoiceNumber}} for {{amount}} is due on {{dueDate}}."
}
]
}

Each PUT increments version. The version is stored for audit purposes — the Audit Service records which template version was used for each notification send.


Delete a Template

DELETE https://api.septemcore.com/v1/notifications/templates/01j9patpl900000000000000
Authorization: Bearer <access_token>

Response 204 No Content. Deleting a template does not affect historical notifications that referenced it — the rendered body is stored in the notification record at send time.


Error Reference

ScenarioHTTPCode
templateId not found404not-found
Template references unregistered channel422CHANNEL_NOT_FOUND
templateId and body both in request400validation-error
Template name already exists409TEMPLATE_NAME_CONFLICT