Notifications API
Omni-channel dispatch, the in-app inbox, notification lists and templates.
For concepts and configuration see the block documentation.
Sending
Section titled “Sending”| Endpoint | Purpose |
|---|---|
POST /notification/{type}/sync | Inline send; returns the provider receipt |
POST /notification/{type}/async | Queue to the outbox; a daemon delivers |
POST /notify/{type}/sync | As above, alternate path |
POST /notify/{type}/async | As above, alternate path |
POST /notify/multi | Send to a user, resolving their channel preferences |
POST /notify/list | Send to a saved list |
{ "templatename": "welcome-onboarding", "data": { "firstname": "Alex" }, "provider_context": { }, "idempotency_key": "optional-caller-supplied-key"}| Field | Required | Notes |
|---|---|---|
templatename | yes | The template to render |
data | no | Values substituted into the template |
provider_context.to[] | for direct sends | Explicit recipients |
user | for /notify/multi | realm:user-ulid; preferences decide the channel |
list | for /notify/list | Saved list id |
idempotency_key | no, but use it | A retry without one is a second real message |
In-app inbox
Section titled “In-app inbox”| Endpoint | Purpose |
|---|---|
PATCH /inapp/user | Paginated list of unseen notifications |
GET /inapp/user/new | Unseen count |
PUT /inapp/user | Mark as seen, by id |
GET /inapp/user/new is the one to poll for a badge — it returns a number rather than a page
of records. These routes are user-scoped and need an end-user JWT; a service key cannot read
another user’s inbox.
Lists and templates
Section titled “Lists and templates”| Endpoint | Purpose |
|---|---|
POST /notificationlist | Create |
GET /notificationlist | List |
PUT /notificationlist | Update |
DELETE /notificationlist/{id} | Delete |
GET /template?name=… | One template |
GET /template | All templates |
Authentication
Section titled “Authentication”Routes accept a service API key, for service-to-service callers authorised through the platform’s agent and bot configuration, or an end-user JWT. The in-app routes require the latter.
Tenancy
Section titled “Tenancy”Every request carries the platform tenancy headers — X-Kis-Tenant, X-Kis-Product and
X-Kis-Environment. They are the partition key for storage, quotas and audit, so a request
without them is rejected rather than served against a default tenant.
Errors
Section titled “Errors”Errors use the platform’s structured error body: a stable code, a human-readable message, and the request id to correlate against logs and audit.
Authentication
Section titled “Authentication”Send routes accept either credential — an API key or a bearer token:
X-Api-Key: <key>Authorization: Bearer <token>The API key is tried first and falls through to the token when absent, so a caller may present
whichever suits it. The metadata and in-app routes (/template, /notificationlist,
/inapp/user) take a bearer token only.
Rate limiting
Section titled “Rate limiting”Send routes are rate limited under named profiles, and the fan-out route has its own:
| Routes | Profile |
|---|---|
/notify/:type/sync, /notify/:type/async, /notify/multi | notify_send |
/notify/list | notify_send_fanout |
/notificationlist, /template reads | notify_metadata_read |
/notificationlist, /template writes | notify_metadata_write |
/inapp/user reads | notify_inapp_read |
/inapp/user writes | notify_inapp_write |
Fan-out is separated deliberately: one /notify/list call can expand into many deliveries, so it is
budgeted apart from single sends. A limited response carries retry_after_seconds — honour it. See
API conventions.
Tenancy
Section titled “Tenancy”The four CPET headers scope templates, notification lists and delivery. /notification/* is an
alias for /notify/* and behaves identically.
Errors
Section titled “Errors”| Status | Meaning |
|---|---|
400 | Request parse or validation failure, or an unknown template |
401 | Missing or invalid credentials, as plain text |
429 | Rate limited — read retry_after_seconds |
500 | Unexpected server error, or a delivery provider failed |