Documentation

Trigger a notification

One call records the event and returns 202. Channel resolution, template rendering and delivery all happen after you have gone back to work.

All documentation
Getting started
Overview Quickstart SDK packages
Frontend
React Vue Svelte React Native Web Components Headless & theming Real-time
Backend API
Authentication Templates Trigger & status Contacts & preferences
More
Self-hosting Reference

One call, every channel

The single front door. Record an event for one or more recipients (e.g. everyone subscribed to a resource) in one call; Elaan resolves the enabled channels, renders templates, and delivers to each asynchronously. Returns 202 immediately with one event_id per recipient.

Request
POST /v1/notifications
Authorization: Bearer sk_…

{
  "notification_type_key": "order_shipped",
  "external_ids": ["crm-12345", "crm-67890"],  // 1–100 recipients
  "branding_key": "acme",        // optional; falls back to the account default
  "variables": { "order_id": "A-1042" }
}
Response · 202 Accepted
{
  "events": [
    { "external_id": "crm-12345", "event_id": "01J…" },
    { "external_id": "crm-67890", "event_id": "01K…" }
  ]
}
  • notification_type_key: the type you created in the console.
  • external_ids: one to 100 recipients (your own user ids); one event is recorded per recipient, each fanning out and reporting status independently. Duplicates are de-duplicated.
  • branding_key: optional. An unknown or omitted brand falls back to the account default.
  • variables: values merged into the template alongside brand values and contact attributes.

Template variables

A template's {{ slots }} are filled from a merged namespace: the recipient's contact attributes, the resolved brand's values, and the per-send variables, with the most specific winning a clash. A slot nobody filled renders as empty rather than failing the send, so adding a slot to a template does not break callers that have not been updated yet.

A variable can also be a list of rows, for invoice lines and order items, rendered with a repeat block in the template:

{
  "notification_type_key": "invoice_ready",
  "external_ids": ["crm-12345"],
  "variables": {
    "total": "£420.00",
    "lines": [
      { "item": "Seat licence", "amount": "£400.00" },
      { "item": "Support",      "amount": "£20.00" }
    ]
  }
}

Rows are flat and do not nest. In the template the block reads each row with item.-prefixed slots.

Delivery status

Because delivery is asynchronous, you can query an event's outcome with the event_id from the trigger response:

GET /v1/notifications/{event_id}
Authorization: Bearer sk_…

// → pending | processed | failed, with any per-channel error summary

Per-channel failures are isolated: a misconfigured email template never blocks the in-app or push delivery of the same event. Each channel records its own outcome on the event, so a partial success is visible as exactly that rather than as a blanket failure.

Retries

Transient send failures are retried with exponential backoff and jitter before the row is dead-lettered, so a provider having a bad ten minutes does not cost you the message. Only failures that cannot be repaired by waiting are terminal immediately.