Skip to main content

Sending Notifications

All notification delivery is asynchronous. send() enqueues the notification to RabbitMQ and returns a notificationId immediately with 202 Accepted. Delivery, retry, and status tracking happen in the background without blocking the caller.


Send a Single Notification

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

const result = await kernel.notify().send({
userId: '01j9pa5mz700000000000000',
channel: 'email',
subject: 'Your invoice is ready',
body: 'Invoice #1042 for $250.00 is ready to download.',
priority: 'normal',
});
// result: { notificationId: '01j9panot700000000000000' }

The method resolves as soon as the notification is enqueued. Delivery happens asynchronously — poll the status endpoint or subscribe to the notify.notification.sent event to confirm delivery.

Full SDK Signature

kernel.notify().send(options: {
userId: string; // recipient — Notify resolves their channel config
channel: string; // 'email' | 'sms' | 'telegram' | 'webhook' | any registered channel
subject?: string; // channel-dependent (required for email, ignored for sms)
body: string; // notification body text
metadata?: Record<string, unknown>; // arbitrary key-value attached to the notification record
priority?: 'low' | 'normal' | 'high' | 'critical'; // default: 'normal'
}): Promise<{ notificationId: string }>

REST Equivalent

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

{
"userId": "01j9pa5mz700000000000000",
"channel": "email",
"subject": "Your invoice is ready",
"body": "Invoice #1042 for $250.00 is ready to download.",
"priority": "normal"
}

Response 202 Accepted:

{
"notificationId": "01j9panot700000000000000",
"status": "queued",
"channel": "email",
"queuedAt": "2026-04-15T10:30:00.000Z"
}

Priority Levels

Priority controls the RabbitMQ queue priority and the delivery SLA. Higher-priority notifications are dequeued before lower-priority ones when the queue is under load.

PriorityRabbitMQ priorityTypical use caseDelivery SLA
low1Marketing, newsletters, digestsBest effort
normal5Order updates, routine alerts< 30 s
high7Payment receipts, security alerts< 5 s
critical9OTP codes, account lockout warnings< 2 s

critical notifications bypass the standard rate limiter and are delivered immediately, even if the tenant has reached the per-minute limit for low/normal/high messages.


Checking Delivery Status

const status = await kernel.notify().status('01j9panot700000000000000');
// { notificationId, status: 'delivered' | 'queued' | 'sending' | 'failed', deliveredAt, channel }
GET https://api.septemcore.com/v1/notifications/01j9panot700000000000000
Authorization: Bearer <access_token>

Response 200 OK:

{
"notificationId": "01j9panot700000000000000",
"status": "delivered",
"channel": "email",
"queuedAt": "2026-04-15T10:30:00.000Z",
"deliveredAt": "2026-04-15T10:30:02.134Z",
"attempts": 1
}

Send a Batch

Send to multiple recipients in a single call:

const result = await kernel.notify().sendBatch({
recipients: [
{ userId: '01j9pa5mz700000000000000', channel: 'email' },
{ userId: '01j9pb3kz100000000000000', channel: 'email' },
],
subject: 'Platform maintenance on Saturday',
body: 'Scheduled downtime 02:00–04:00 UTC on 2026-04-19.',
priority: 'normal',
});
// result: { jobId: '01j9pajob800000000000000', total: 2 }
POST https://api.septemcore.com/v1/notifications/batch
Authorization: Bearer <access_token>
Content-Type: application/json

{
"recipients": [
{ "userId": "01j9pa5mz700000000000000", "channel": "email" },
{ "userId": "01j9pb3kz100000000000000", "channel": "email" }
],
"subject": "Platform maintenance on Saturday",
"body": "Scheduled downtime 02:00–04:00 UTC on 2026-04-19.",
"priority": "normal"
}

Response 202 Accepted:

{
"jobId": "01j9pajob800000000000000",
"total": 2,
"status": "processing"
}

Batch limits

ParameterValue
Recipients per batch callMax 500
Batch → background job threshold> 100 recipients
Response for ≤ 100 recipients202 Accepted + array of notificationId
Response for > 100 recipients202 Accepted + { jobId } (background job)

When a batch triggers a background job, track progress:

GET https://api.septemcore.com/v1/notifications/jobs/01j9pajob800000000000000
Authorization: Bearer <access_token>
{
"jobId": "01j9pajob800000000000000",
"status": "processing",
"sent": 87,
"failed": 3,
"total": 500
}

status transitions: queuedprocessingcompleted | completed_with_errors.


Send from Template

await kernel.notify().sendFromTemplate({
templateId: '01j9patpl900000000000000',
userId: '01j9pa5mz700000000000000',
variables: {
name: 'Alice Chen',
invoiceNumber: '1042',
amount: '$250.00',
dueDate: '2026-04-30',
},
priority: 'normal',
});
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"
},
"priority": "normal"
}

templateId and body are mutually exclusive — you cannot provide both in the same request.


Notification History

GET https://api.septemcore.com/v1/notifications?limit=20&cursor=01j9panot...
Authorization: Bearer <access_token>
{
"data": [
{
"notificationId": "01j9panot700000000000000",
"channel": "email",
"status": "delivered",
"deliveredAt": "2026-04-15T10:30:02Z"
}
],
"pagination": {
"nextCursor": "01j9panot600000000000000",
"hasMore": true
}
}

History is stored for 90 days. Failed notifications are separately accessible:

GET https://api.septemcore.com/v1/notifications/failed?limit=20
Authorization: Bearer <access_token>

Manual Retry

POST https://api.septemcore.com/v1/notifications/01j9panot700000000000000/retry
Authorization: Bearer <access_token>

Response 202 Accepted:

{
"notificationId": "01j9panot700000000000000",
"status": "queued",
"attempt": 6
}

Manual retry is not subject to the standard 5-attempt limit — it always re-enqueues the notification regardless of previous attempts.


Error Reference

ScenarioHTTPCode
Channel not registered400CHANNEL_NOT_FOUND
User not found404not-found
Rate limit exceeded (non-critical)429rate-limit-exceeded — notification queued with delay
Batch > 500 recipients400BATCH_LIMIT_EXCEEDED
Batch job — queue full (>10 000)503queue-full
templateId and body both provided400validation-error