Events & Templates
Learn how to define versioned event contracts, design multi-channel templates in Template Studio, and dispatch notifications with variable interpolation.
Events & Templates Architecture
In Sendrin, sending a notification is decoupled into two clean primitives:
[ Backend Application ]
│
▼ (Dispatches notification with event_key + data payload)
[ Event Contract (/events) ]
│ ── Validates payload against JSON Schema
│ ── Enforces allowed delivery channels
▼
[ Published Template (/templates) ]
│ ── Interpolates {{variables}} into HTML / SMS / WhatsApp / In-App
▼
[ Transport Provider (Postal SMTP / Meta Cloud / SMS Gateway) ]
│
▼
[ Recipient Inbox / Device ]- Event Contract (
/events): A versioned business contract (e.g.order.confirmed,user.welcome) that declares required payload properties (e.g.customer_name,order_id,amount) and allowed channels. - Channel Template (
/templates): The presentation layer for a specific channel (Email HTML, SMS text, WhatsApp approved template, or In-App banner) that interpolates variables using Handlebars syntax:{{customer_name}}.
Step 1: Register an Event Contract
Before creating a template, define the event contract in the Sendrin Console under Events.
1-Click Starter Presets
The Console provides pre-configured contracts with auto-filled JSON schemas:
| Starter Preset | Event Key | Default Channels | Key Variables |
|---|---|---|---|
| User Welcome & Onboarding | user.welcome | email, in_app | {{user_name}}, {{login_url}} |
| Order Confirmation | order.confirmed | email, sms, whatsapp | {{customer_name}}, {{order_id}}, {{total_amount}}, {{tracking_url}} |
| Billing & Invoice Ready | billing.invoice_ready | email | {{customer_name}}, {{invoice_number}}, {{amount_due}}, {{pdf_url}} |
| Password Reset | auth.password_reset | email | {{user_name}}, {{reset_link}}, {{expires_in_minutes}} |
| Custom Contract | User-defined | Flexible | Custom defined schema |
Example JSON Schema
Here is the JSON Schema for an order.confirmed contract:
{
"type": "object",
"required": ["customer_name", "order_id", "total_amount"],
"properties": {
"customer_name": { "type": "string", "description": "Customer full name" },
"order_id": { "type": "string", "description": "Order identifier #" },
"total_amount": { "type": "string", "description": "Total amount (e.g. ₹1,499)" },
"tracking_url": { "type": "string", "description": "Shipment tracking URL" }
},
"additionalProperties": true
}[!TIP] Setting
"additionalProperties": trueis recommended. This allows your backend to pass metadata fields (such as timestamps or internal IDs) without failing schema validation.
Step 2: Inspecting Created Events
Once created, click any event row in the Events list to open the Event Details Drawer:
- Schema Variables: Displays all variables, data types, and required badges.
- Sample Payload: Automatically synthesizes realistic mock data based on the schema for quick testing.
- API Integration Snippet: Generates ready-to-run copy-paste snippets in cURL, Node.js, and Python.
- Design Template: Direct shortcut to open Template Studio with this event pre-selected.
Step 3: Designing Templates in Template Studio
Navigate to Templates and click + Open Studio (or click Design Template from an Event):
- Select the Event: Choose the event contract (e.g.,
order.confirmed). - Select the Channel: Email, SMS, WhatsApp, or In-App.
- Use Handlebars Variables: Insert dynamic fields using
{{variable_name}}.
Email Template Example
Subject line:
Order Confirmed: #{{order_id}}HTML Content:
<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
<h2>Thank you for your order, {{customer_name}}!</h2>
<p>We've received order <strong>#{{order_id}}</strong> for a total of <strong>{{total_amount}}</strong>.</p>
<div style="margin: 30px 0;">
<a href="{{tracking_url}}" style="background: #ff6b00; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: bold;">
Track Your Package
</a>
</div>
<p style="color: #64748b; font-size: 13px;">If you have any questions, reply directly to this email.</p>
</div>The 3-Step Template Lifecycle
Templates follow an immutable versioning lifecycle to ensure production safety:
[ 1. Draft ] ──(Save changes)──> [ 2. Validate ] ──(Validate Schema)──> [ 3. Publish to Live ]- Draft: Create and edit content. Drafts cannot be dispatched by the live API.
- Validate: Verifies that all variables used in the template exist in the event schema.
- Publish to Live: Promotes the validated version to the active live version. Only published versions are sent by
POST /api/v1/notifications.
Step 4: Dispatching Notifications in Code
Once your template is published, dispatch notifications using your Application API Key.
cURL
curl --request POST 'https://api.sendrin.com/api/v1/notifications' \
--header "Authorization: Bearer $SENDRIN_API_KEY" \
--header 'Content-Type: application/json' \
--header "Idempotency-Key: idemp_order_1001" \
--data '{
"event_key": "order.confirmed",
"channel": "email",
"recipient": {
"email": "customer@example.com"
},
"data": {
"customer_name": "Ravi Kant",
"order_id": "ORD-98214",
"total_amount": "₹2,499",
"tracking_url": "https://sendrin.com/track/ORD-98214"
}
}'Node.js (TypeScript / JavaScript)
const response = await fetch("https://api.sendrin.com/api/v1/notifications", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SENDRIN_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `order_${orderId}`,
},
body: JSON.stringify({
event_key: "order.confirmed",
channel: "email",
recipient: {
email: customerEmail,
},
data: {
customer_name: customerName,
order_id: orderId,
total_amount: "₹2,499",
tracking_url: `https://sendrin.com/track/${orderId}`,
},
}),
});
const result = await response.json();
console.log("Notification accepted:", result.id, "Status:", result.status);Python (requests)
import os
import requests
response = requests.post(
"https://api.sendrin.com/api/v1/notifications",
headers={
"Authorization": f"Bearer {os.environ['SENDRIN_API_KEY']}",
"Content-Type": "application/json",
"Idempotency-Key": f"order_{order_id}",
},
json={
"event_key": "order.confirmed",
"channel": "email",
"recipient": {
"email": "customer@example.com"
},
"data": {
"customer_name": "Ravi Kant",
"order_id": "ORD-98214",
"total_amount": "₹2,499",
"tracking_url": "https://sendrin.com/track/ORD-98214"
},
},
timeout=30,
)
result = response.json()
print(f"Accepted notification: {result['id']}")Troubleshooting & Common Errors
422 Unprocessable Entity - schema_validation_failed
- Cause: The
datapayload sent to/notificationsdid not match the JSON schema registered on the event. - Fix: Check the Event Details Drawer in the Console under Schema Variables to verify all required variables are supplied with matching types.
409 Conflict - template_not_published
- Cause: The template mapped to the event and channel is currently in
draftstate. - Fix: Open the template in Template Studio, click Validate, and then click Publish to Live.
401 Unauthorized - invalid_api_key
- Cause: The API key header is missing or incorrect.
- Fix: Under Credentials, copy your application secret key (
sendrin_live_sk_...) and ensure it is stored asSENDRIN_API_KEYon your backend.