Core Concepts
This page explains the key terms you'll encounter in Orqestra using plain language and real-world analogies. Bookmark it — these concepts underpin everything else in the docs.
Tenant
A tenant is a team or application that uses Orqestra independently.
Think of it like a separate mailbox. Each tenant has:
- Their own API key — the credential they use to trigger events
- Their own templates — the messages they've authored
- Their own provider settings — the email/SMS accounts they've connected
- Their own logs — they can only see their own delivery history
One Orqestra instance can serve many tenants without any data leaking between them. If you're a SaaS company, each of your customers could be a separate tenant.
Provider
A provider is the service that actually delivers the message.
Orqestra doesn't send emails or SMS messages directly. It delegates to third-party delivery services:
| Channel | Providers |
|---|---|
| Resend, SendGrid | |
| SMS | Twilio, Africa's Talking |
| Push | Built-in real-time (no external provider needed) |
You bring your own API key. Orqestra stores it encrypted and routes your notifications through it. You keep the billing relationship with the provider and can switch providers without changing your application code.
Event
An event is a signal from your app that something happened.
Examples: user.signed_up, order.confirmed, payment.failed, subscription.cancelled.
Your application fires events by making an HTTP call to Orqestra:
{
"eventType": "order.confirmed",
"payload": {
"userId": "alice-uuid",
"recipientEmail": "alice@example.com"
},
"variables": {
"orderId": "ORD-12345",
"total": 89.00
}
}
payload tells Orqestra who to notify. variables tells Orqestra what to say — these values are interpolated into your template at render time. Orqestra receives the event, finds matching templates, and dispatches notifications. Your application doesn't need to know how the message is formatted, which provider sends it, or whether it goes to email, SMS, or both.
Payload and Variables
Every event request has two distinct data containers. They serve different purposes and the engine treats them differently.
payload — routing identity
payload tells Orqestra who the recipient is and how to reach them. These are the only fields the engine itself inspects. Any extra fields placed here will be rejected with a validation error.
| Field | Required | Description |
|---|---|---|
userId | Yes | The recipient's ID in your system. Used for in-app push delivery and notification history. |
recipientEmail | No — required for Email | The email address to deliver to. Must be a valid email address. |
recipientPhone | No — required for SMS | The phone number to deliver to. Must be in E.164 format (e.g. +254712345678). |
"payload": {
"userId": "user_abc123",
"recipientEmail": "alice@example.com",
"recipientPhone": "+254712345678"
}
:::tip Which payload fields are required?
Only userId is always required. recipientEmail is required when you have an active Email template for the event. recipientPhone is required when you have an active SMS template. If a routing field is missing and a matching channel template exists, that channel is skipped.
:::
variables — template data
variables is a free-form object of key/value pairs that your template renders. The engine does not validate or inspect the contents — the shape is entirely up to you and whatever your template expects.
Every key in variables becomes a Handlebars expression in your template:
| What you send | What your template uses |
|---|---|
"variables": { "orderNumber": "ORD-123" } | {{orderNumber}} |
"variables": { "customer": { "name": "Alice" } } | {{customer.name}} |
"variables": { "items": ["Shoes", "Hat"] } | {{#each items}}{{this}}{{/each}} |
Values can be strings, numbers, booleans, objects, or arrays — anything that Handlebars can render.
"variables": {
"orderNumber": "ORD-2026-1234",
"amount": "$89.00",
"customerName": "Alice",
"isPremium": true,
"items": ["Sneakers", "Cap"]
}
:::note There are no reserved keys in variables
The only convention is for global.* event types, which expect at minimum title (string) and message (string) — these are what the built-in platform templates render. For your own custom event types, the keys are entirely determined by what your template uses.
:::
Putting it together
{
"eventType": "order.confirmed",
"payload": {
"userId": "user_abc123",
"recipientEmail": "alice@example.com"
},
"variables": {
"customerName": "Alice",
"orderNumber": "ORD-2026-1234",
"amount": "$89.00",
"estimatedDelivery": "April 30, 2026"
}
}
The template for this event might read:
Hi {{customerName}}, your order {{orderNumber}} totalling {{amount}}
is confirmed. Estimated delivery: {{estimatedDelivery}}.
The routing fields from payload (userId, recipientEmail, recipientPhone) are also accessible in templates as a fallback, but template-specific content should always live in variables.
Template
A template is a reusable message layout.
Each template is linked to an event type and a channel (email, SMS, or push). When an event fires, Orqestra finds all active templates that match the event type and renders them — substituting every {{fieldName}} expression with the matching value from variables (or from payload as a fallback for routing fields).
For example, a template for order.confirmed on the EMAIL channel might have:
- Subject:
Your order {{orderId}} is confirmed—orderIdcomes fromvariables - Body: A responsive HTML email using
{{customerName}},{{total}},{{recipientEmail}}, etc.
All keys in variables are available by name. The three routing fields from payload (userId, recipientEmail, recipientPhone) are also accessible in case your template needs them.
Templates use Handlebars syntax for variables ({{variableName}}). Email templates support MJML for responsive layouts that render correctly across email clients.
:::tip Activate your templates A template must be set to Active before it fires. Creating a template does nothing until you turn it on. :::
Channel
A channel is the delivery method.
Orqestra supports three channels:
| Channel | Description |
|---|---|
| Delivered via your configured email provider (Resend, SendGrid) | |
| SMS | Delivered via your configured SMS provider (Twilio, Africa's Talking) |
| Push | Real-time in-app notification delivered via WebSocket — no external provider needed |
A single event can trigger notifications on multiple channels simultaneously. For example, a payment.failed event could send both an email (with full details) and an SMS (with a brief summary) at the same time, as long as you have templates for both channels.
Dead Letter Queue (DLQ) / Recovery Queue
When a notification fails permanently, it doesn't disappear — it goes to the Recovery Queue.
Orqestra automatically retries failed notifications several times. If all retries are exhausted, the notification moves to the Recovery Queue (also called the Dead Letter Queue or DLQ in technical contexts).
From the Recovery Queue in the Admin UI you can:
- See the exact error that caused the failure
- Fix the root cause (e.g. update your API key, fix a template variable)
- Replay the notification — Orqestra will try again
Nothing is lost silently. If a notification doesn't deliver, you'll see exactly why in the Recovery Queue.
Idempotency
Sending the same event twice won't create duplicate notifications.
Every event trigger can include an optional idempotencyKey in the request headers. If you send the same key twice within 24 hours, Orqestra silently deduplicates the second request — useful if your service retries HTTP calls on failure.
See Sending Events → Idempotency for how to use this.
Sandbox
The sandbox is a safe environment for testing.
The sandbox is a pre-configured Orqestra tenant with demo credentials. You can fire events, create templates, and inspect logs without affecting production. Session duration is limited and delivery uses a demo sender, so test messages won't land in real inboxes.
See the Quickstart to use the sandbox.