Notifications
Notifications are how Protocol keeps your users informed across every channel -- email, SMS, push, Slack, Teams, and webhooks. Powered by template-based rendering with Handlebars interpolation, Redis-backed rate limiting per channel, and delivery tracking with automatic retry, the notification system ensures reliable, observable message delivery at scale. On this page, we will explore the endpoints for sending and managing notifications programmatically.
The notification model
The notification model contains all the information about a single notification delivery, including the channel, template, recipient, and delivery status.
Channels
Six delivery channels are supported: email (SMTP), sms (Twilio), push (FCM), slack (Slack API), teams (Microsoft Teams), and webhook (HTTP POST). Each channel has independent rate limits enforced through Redis token buckets.
Properties
- Name
id- Type
- string
- Description
Unique identifier for the notification.
- Name
channel- Type
- string
- Description
Delivery channel. One of
email,sms,push,slack,teams, orwebhook.
- Name
template_id- Type
- string
- Description
Reference to the notification template used for rendering.
- Name
recipient- Type
- string
- Description
The recipient address -- email, phone number, user ID, or webhook URL depending on the channel.
- Name
status- Type
- string
- Description
Current delivery status. One of
pending,sent,delivered,failed, orread.
- Name
subject- Type
- string
- Description
The notification subject line (used by email and push channels).
- Name
body- Type
- string
- Description
The rendered notification content after template interpolation.
- Name
sent_at- Type
- timestamp
- Description
Timestamp of when the notification was sent.
- Name
read_at- Type
- timestamp
- Description
Timestamp of when the notification was read by the recipient.
Send a notification
This endpoint allows you to send a notification to a recipient through any supported channel. The notification body is rendered from a template using Handlebars interpolation with the provided variables. Rate limiting is enforced per channel via Redis token buckets.
Required attributes
- Name
channel- Type
- string
- Description
The delivery channel. One of
email,sms,push,slack,teams, orwebhook.
- Name
recipient- Type
- string
- Description
The recipient address for the chosen channel.
- Name
template_id- Type
- string
- Description
The ID of the notification template to render.
Optional attributes
- Name
variables- Type
- object
- Description
Key-value pairs for Handlebars template interpolation.
- Name
subject- Type
- string
- Description
Override the template subject line.
Request
curl http://localhost:3000/v1/notifications/send \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{\
"channel": "email",\
"recipient": "jane.doe@company.com",\
"template_id": "tmpl_ticket_assigned",\
"variables": {\
"ticket_id": "TKT-4829",\
"assignee_name": "Jane Doe",\
"priority": "P1"\
}\
}'
Response
{
"id": "ntf_9xKmPqRsT2uVwX",
"channel": "email",
"template_id": "tmpl_ticket_assigned",
"recipient": "jane.doe@company.com",
"status": "sent",
"subject": "[P1] Ticket TKT-4829 assigned to you",
"body": "Hi Jane Doe, ticket TKT-4829 has been assigned to you with priority P1.",
"sent_at": 1708732800,
"read_at": null
}
List all notifications
This endpoint allows you to retrieve a paginated list of all notifications. By default, a maximum of ten notifications are shown per page. You can filter by channel and status.
Optional attributes
- Name
channel- Type
- string
- Description
Filter notifications by delivery channel.
- Name
status- Type
- string
- Description
Filter notifications by delivery status.
- Name
limit- Type
- integer
- Description
Limit the number of notifications returned.
Request
curl -G http://localhost:3000/v1/notifications \
-H "Authorization: Bearer {token}" \
-d channel="email" \
-d limit=10
Response
{
"has_more": false,
"data": [
{
"id": "ntf_9xKmPqRsT2uVwX",
"channel": "email",
"template_id": "tmpl_ticket_assigned",
"recipient": "jane.doe@company.com",
"status": "delivered",
"subject": "[P1] Ticket TKT-4829 assigned to you",
"body": "Hi Jane Doe, ticket TKT-4829 has been assigned to you with priority P1.",
"sent_at": 1708732800,
"read_at": 1708733400
},
{
"id": "ntf_3aB4cDeFgH5iJk"
// ...
}
]
}
Get unread count
This endpoint returns the count of unread notifications for the authenticated user. Use this for badge counts and notification indicators in your UI.
Request
curl http://localhost:3000/v1/notifications/unread \
-H "Authorization: Bearer {token}"
Response
{
"unread_count": 7,
"by_channel": {
"email": 3,
"slack": 2,
"push": 1,
"teams": 1
}
}
Mark as read
This endpoint marks a notification as read. The read_at timestamp is set to the current time. This is idempotent -- calling it on an already-read notification returns the existing read timestamp.
Request
curl -X PUT http://localhost:3000/v1/notifications/ntf_9xKmPqRsT2uVwX/read \
-H "Authorization: Bearer {token}"
Response
{
"id": "ntf_9xKmPqRsT2uVwX",
"status": "read",
"read_at": 1708733400
}
Get notification preferences
This endpoint retrieves the notification preferences for the authenticated user. Preferences control which channels are enabled, quiet hours, and per-event opt-in/opt-out settings.
Request
curl http://localhost:3000/v1/notifications/preferences \
-H "Authorization: Bearer {token}"
Response
{
"channels": {
"email": { "enabled": true, "quiet_hours": null },
"sms": { "enabled": false, "quiet_hours": null },
"push": { "enabled": true, "quiet_hours": { "start": "22:00", "end": "07:00" } },
"slack": { "enabled": true, "quiet_hours": null },
"teams": { "enabled": false, "quiet_hours": null },
"webhook": { "enabled": false, "quiet_hours": null }
},
"events": {
"ticket_assigned": ["email", "push", "slack"],
"ticket_resolved": ["email"],
"sla_breach": ["email", "push", "slack", "sms"]
}
}
Update notification preferences
This endpoint updates the notification preferences for the authenticated user. You can enable or disable channels, configure quiet hours, and set per-event channel preferences.
Optional attributes
- Name
channels- Type
- object
- Description
Channel configuration with
enabledandquiet_hourssettings per channel.
- Name
events- Type
- object
- Description
Map of event types to arrays of enabled channel names.
Request
curl -X PUT http://localhost:3000/v1/notifications/preferences \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{\
"channels": {\
"sms": { "enabled": true },\
"push": { "quiet_hours": { "start": "23:00", "end": "08:00" } }\
},\
"events": {\
"ticket_assigned": ["email", "push", "slack", "sms"]\
}\
}'
Response
{
"channels": {
"email": { "enabled": true, "quiet_hours": null },
"sms": { "enabled": true, "quiet_hours": null },
"push": { "enabled": true, "quiet_hours": { "start": "23:00", "end": "08:00" } },
"slack": { "enabled": true, "quiet_hours": null },
"teams": { "enabled": false, "quiet_hours": null },
"webhook": { "enabled": false, "quiet_hours": null }
},
"events": {
"ticket_assigned": ["email", "push", "slack", "sms"],
"ticket_resolved": ["email"],
"sla_breach": ["email", "push", "slack", "sms"]
}
}
List notification templates
This endpoint returns a paginated list of available notification templates. Templates use Handlebars syntax for dynamic content interpolation and can be associated with one or more channels.
Optional attributes
- Name
channel- Type
- string
- Description
Filter templates by supported channel.
- Name
limit- Type
- integer
- Description
Limit the number of templates returned.
Request
curl -G http://localhost:3000/v1/notifications/templates \
-H "Authorization: Bearer {token}" \
-d limit=10
Response
{
"has_more": false,
"data": [
{
"id": "tmpl_ticket_assigned",
"name": "Ticket Assigned",
"channels": ["email", "push", "slack"],
"subject": "[{{priority}}] Ticket {{ticket_id}} assigned to you",
"body": "Hi {{assignee_name}}, ticket {{ticket_id}} has been assigned to you with priority {{priority}}.",
"variables": ["ticket_id", "assignee_name", "priority"],
"created_at": 1708646400,
"updated_at": 1708732800
},
{
"id": "tmpl_sla_breach",
"name": "SLA Breach Warning",
"channels": ["email", "push", "slack", "sms"],
"subject": "SLA Breach: Ticket {{ticket_id}}",
"body": "Ticket {{ticket_id}} is about to breach its {{sla_type}} SLA. Time remaining: {{time_remaining}}.",
"variables": ["ticket_id", "sla_type", "time_remaining"],
"created_at": 1708646400,
"updated_at": 1708646400
}
]
}