Notification preferences
Per-(customer × event_type × channel) opt-in/opt-out toggles. The single check that decides "should this customer receive this email?".
How it's used
The send service calls should_notify(customer, event_type, channel)
before every transactional message. If it returns false, the send
is short-circuited (no Resend call, 409 Conflict returned to the
caller).
Resolution order
1. exact row (customer, event_type, channel)
2. wildcard row (customer, '<namespace>.*', channel)
3. default argument (caller-provided fallback)
The wildcard pattern matches the namespace prefix:
Stored event_type | Matches |
|---|---|
marketing.* | marketing.weekly_digest, marketing.flash_sale, ... |
order.* | order.confirmation, order.shipped, ... |
* | All events for this channel |
A specific row always wins over a wildcard. So a customer can opt
out of all marketing (marketing.*: false) but still opt in to one
specific marketing email (marketing.flash_sale: true).
Set a preference
curl -X PUT "$API/customers/{customer_id}/notifications.json" \
-H "Authorization: Bearer $NEVIOS_KEY" -H "Content-Type: application/json" \
-d '{
"event_type": "marketing.*",
"channel": "email",
"enabled": false,
"source": "unsubscribe_link",
"reason": "User clicked unsubscribe link in 2026-04-15 newsletter"
}'
source indicates who changed the preference (customer, staff,
api, unsubscribe_link). Useful for compliance audits.
List a customer's preferences
curl "$API/customers/{customer_id}/notifications.json" \
-H "Authorization: Bearer $NEVIOS_KEY"
Check resolution (debug)
curl "$API/customers/{customer_id}/notifications/check.json?event_type=marketing.weekly&channel=email&default=true" \
-H "Authorization: Bearer $NEVIOS_KEY"
{
"customer_id": "308...",
"event_type": "marketing.weekly",
"channel": "email",
"enabled": false // matched a 'marketing.*' wildcard row
}
Channels
| Channel | Notes |
|---|---|
email | Phase 1 — primary |
sms | Reserved (carrier integrations not yet shipped) |
push | Reserved (mobile app push) |
in_app | Reserved (dashboard inbox-style notifications) |
Compliance use cases
- Unsubscribe link: clicking it sets
marketing.*: falsefor that customer - Account closure: the GDPR redaction flow doesn't touch preferences (the customer's email is anonymized so nothing can reach them anyway)
- Customer self-service:
PUTfrom the dashboard's preference panel - Staff override: rarely needed — set with
source: "staff"for audit
Endpoints
PUT /admin/2026-01/{handle}/customers/{customer_id}/notifications.json
GET /admin/2026-01/{handle}/customers/{customer_id}/notifications.json
GET /admin/2026-01/{handle}/customers/{customer_id}/notifications/check.json
Next: Email loop.