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.
| Priority | RabbitMQ priority | Typical use case | Delivery SLA |
|---|---|---|---|
low | 1 | Marketing, newsletters, digests | Best effort |
normal | 5 | Order updates, routine alerts | < 30 s |
high | 7 | Payment receipts, security alerts | < 5 s |
critical | 9 | OTP 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
| Parameter | Value |
|---|---|
| Recipients per batch call | Max 500 |
| Batch → background job threshold | > 100 recipients |
| Response for ≤ 100 recipients | 202 Accepted + array of notificationId |
| Response for > 100 recipients | 202 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: queued → processing → completed | 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
| Scenario | HTTP | Code |
|---|---|---|
| Channel not registered | 400 | CHANNEL_NOT_FOUND |
| User not found | 404 | not-found |
| Rate limit exceeded (non-critical) | 429 | rate-limit-exceeded — notification queued with delay |
| Batch > 500 recipients | 400 | BATCH_LIMIT_EXCEEDED |
| Batch job — queue full (>10 000) | 503 | queue-full |
| templateId and body both provided | 400 | validation-error |