SeptemCore LogoSeptemCore
API ReferenceREST API

Notify REST API Reference

Notify Service REST API reference. 10 endpoints: send, batch, retry, status, history, failed queue, templates CRUD, channels CRUD, and async batch jobs. RabbitMQ-backed delivery with exponential retry. WebSocket real-time protocol reference. Rate limiting per tenant and per module.

The Notify Service is the single delivery hub for all platform notifications. Delivery channels are plugin-based — the core provides the NotificationChannel interface and adapters (Email, SMS, Telegram, Slack, WebSocket, Browser Push, Webhook) are installed as modules.

Built-in channels that require no adapter: WebSocket and Browser Push (FCM/APNs).

See the REST API Overview for authentication, error format, pagination, and rate limiting. See the WebSocket Protocol for the full real-time specification.

For layout brevity, the /api/v1 base path prefix is omitted from the endpoint tables below.


Endpoints

EndpointTypeDescription
POST /notifyCRUDSend a notification
POST /notify/batchActionSend to multiple recipients
GET /notify/:idCRUDNotification status
GET /notifyCRUDNotification history (paginated)
POST /notify/:id/retryActionRetry a failed notification
GET /notify/failedCRUDFailed notifications queue (paginated)
POST /notify/templatesCRUDCreate a notification template
GET /notify/templatesCRUDList templates
POST /notify/channelsCRUDRegister a channel adapter
GET /notify/channelsCRUDList registered channels

POST /api/v1/notify — Send

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

{
  "channel":   "email",
  "recipient": {
    "userId": "01j9pusr0000000000000001"
  },
  "subject": "Your invoice is ready",
  "body":    "Invoice #INV-2026-042 is available in your account.",
  "priority": "normal",
  "meta": { "invoiceId": "inv_042" }
}

Response 202 Accepted (fire-and-forget — delivery happens asynchronously):

{
  "notificationId": "01j9pnot0000000000000001",
  "status":         "queued",
  "channel":        "email",
  "createdAt":      "2026-04-22T04:10:00Z"
}

POST /notify always returns 202 Accepted immediately. kernel.notify().send() is non-blocking — delivery and retries happen via RabbitMQ queue asynchronously.

Request Fields

FieldRequiredDescription
channelRegistered channel name (e.g. email, sms, telegram, websocket)
recipient.userId✅ (or recipient.address)Platform user ID — core resolves linked channels
recipient.address✅ (or recipient.userId)Direct address (e.g. raw email or phone number)
subjectSubject line (email, push)
bodyNotification body text
prioritylow, normal (default), high, critical
templateIdIf set, body may be omitted — template variables applied via variables
variablesKey-value map for template substitution
metaArbitrary JSON metadata stored with the record

POST /api/v1/notify/batch — Batch Send

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

{
  "channel":    "email",
  "recipients": [
    { "userId": "01j9pusr0000000000000001" },
    { "userId": "01j9pusr0000000000000002" }
  ],
  "templateId":  "tmpl_invoice_ready",
  "variables":   { "planName": "Growth" },
  "priority":    "normal"
}
Batch limitValue
Max recipients per batch call500
Exceeded400 Bad Request (problems/batch-limit-exceeded)
The response returns 202 Accepted with the enqueued notification IDs
immediately:
{
  "ids": [
    "01j9pnot0000000000000001",
    "01j9pnot0000000000000002"
  ],
  "count": 2
}

GET /api/v1/notify/:id — Status

{
  "id":         "01j9pnot0000000000000001",
  "channel":    "email",
  "recipient":  { "userId": "01j9pusr0000000000000001", "address": "[email protected]" },
  "subject":    "Your invoice is ready",
  "status":     "delivered",
  "priority":   "normal",
  "retries":    0,
  "sentAt":     "2026-04-22T04:10:02Z",
  "deliveredAt": "2026-04-22T04:10:04Z",
  "failedAt":   null,
  "meta":       { "invoiceId": "inv_042" }
}
Status valueMeaning
queuedIn RabbitMQ, not yet attempted
sendingAdapter is making the delivery attempt
deliveredAdapter confirmed delivery
failedMax retries exhausted — see failed queue

POST /api/v1/notify/:id/retry — Manual Retry

Manually re-enqueue a failed notification:

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

Response 202 Accepted — notification re-queued.


GET /api/v1/notify/failed — Failed Queue

Lists notifications that exhausted all retry attempts (cursor-paginated):

GET https://api.septemcore.com/v1/notify/failed
  ?channel=email
  &since=2026-04-01T00:00:00Z
  &page_size=20
Authorization: Bearer <access_token>

Retry Policy

Delivery retries happen transparently via RabbitMQ with exponential backoff. The calling service is never blocked.

ParameterValue
Max retries5 (per-adapter, configurable)
Backoff schedule30 s → 60 s → 120 s → 240 s → 480 s (±10% jitter)
Timeout per attempt10 s (per-adapter, configurable)
After max retriesStatus: failed. Recorded in notifications_failed table. Visible in Admin UI → Notifications → Failed
Manual retryPOST /notify/:id/retry
Automatic fallbackNot provided — no automatic channel fallback

Note: Retry does NOT block the caller. POST /notify returns 202 Accepted immediately. RabbitMQ queue per tenant: notify.outgoing.{tenantId} (one slow tenant cannot affect others). Queue policy: x-max-length: 10000, overflow: reject-publish503.


Templates

Templates store reusable notification content with variable substitution.

EndpointDescription
POST /notify/templatesCreate template
GET /notify/templatesList templates

POST /api/v1/notify/templates

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

{
  "id":       "tmpl_invoice_ready",
  "name":     "Invoice Ready",
  "channel":  "email",
  "subject":  "Your {{planName}} invoice is ready",
  "body":     "Invoice {{invoiceNumber}} for your {{planName}} plan is now available.",
  "variables": ["planName", "invoiceNumber"]
}

Variable syntax: {{variableName}}. All declared variables must be provided in POST /notify variables map — missing variables → 400 Bad Request.


Channels

Channels are installed by registering adapter modules via module.manifest.json (notificationChannels field). After installation, the channel automatically appears in Settings → Notifications.

EndpointDescription
GET /notify/channelsList registered channels
POST /notify/channelsRegister a custom channel adapter

Channel Object

{
  "id":       "email",
  "name":     "Email (SendGrid)",
  "type":     "email",
  "iconUrl":  "https://cdn.platform.io/icons/sendgrid.svg",
  "status":   "active",
  "config": {
    "apiKey":   "[redacted]",
    "fromEmail": "[email protected]"
  },
  "registeredAt": "2026-04-01T09:00:00Z"
}

Built-in channels (no adapter required):

Channel IDTypeNotes
websocketreal-timenhooyr.io/websocket. See WebSocket Protocol
pushbrowser pushService Worker + FCM/APNs

Plugin channels (installed as modules):

AdapterType
Email (SMTP / SendGrid / Mailgun)email
SMS (Twilio / Vonage)sms
Telegramsocial
Slacksocial
Discordsocial
Webhookwebhook

Rate Limiting

LimitValueEnv var
Per-tenant100 notifications / minuteNOTIFY_RATE_LIMIT_PER_TENANT
Per-module50 notifications / minute
Batch max recipients500
Exceeded429 Too Many Requests — message is not lost, queued with delay

Principle: Without rate limiting, a buggy module loop (send() for 100K users) would get the platform's IP banned by SendGrid/Telegram, affecting all tenants.


Notification History Retention

DataRetentionStorage
Notification history90 days (NOTIFY_HISTORY_RETENTION_DAYS)PostgreSQL
Failed notifications180 daysPostgreSQL
Long-term audit trail7 yearsAudit Service (every send recorded)

Error Reference

Error typeStatusTrigger
problems/channel-not-found404Specified channel is not registered for this tenant
problems/batch-limit-exceeded400More than 500 recipients in one batch call
problems/template-not-found404templateId does not exist
problems/template-variable-missing400Required template variable not provided
problems/notification-not-failed400Retry attempted on a non-failed notification
problems/rate-limit-exceeded429Per-tenant or per-module notification rate exceeded
problems/queue-full503RabbitMQ per-tenant queue at x-max-length: 10000

On this page