Policies
An event lands in the store no matter what (see Events & the envelope). Whether it also triggers a notification — and on which lane, through which channels, to whom — is decided by a per-tenant policy engine, evaluated for every event after it's persisted.
The decision
type Decision struct {
Notify bool
Delivery Delivery // "instant" | "batched" — meaningless when Notify is false
Channels []string
Recipients []string // final fan-out set
}
Recipients is the event's own target.subject_ids plus any policy-configured
additions, deduplicated — so a rule can notify a fixed group (e.g. an ops channel)
every time a given event_type fires, on top of whoever the event itself targeted.
Authoring a rule
Rules are keyed by event_type, with a literal "*" as the tenant's default/fallback
rule:
{
"event_type": "invoice.paid", // or "*" for the default
"notify": true,
"delivery": "instant", // omit to fall back to the event's own delivery lane
"channels": ["inapp"], // informational in Phase 1 — see Channels
"extra_recipients": ["sub_finance_team"]
}
Managed through the Admin API (tenant-admin role):
| Method | Path |
|---|---|
GET | /admin/v1/policies |
POST | /admin/v1/policies — upsert; body's event_type selects the rule id |
GET | /admin/v1/policies/{event_type} |
PUT | /admin/v1/policies/{event_type} — path wins over any event_type in the body |
DELETE | /admin/v1/policies/{event_type} |
Full shapes: API Reference → Admin API. Step-by-step: Guides → Authoring notification policies.
What "evaluated per event" means in practice
Matching is exact event_type, falling back to the tenant's "*" rule if no exact
match exists. No match at all, or an explicit notify: false, means the event is
still stored — it simply doesn't fan out. This is a deliberate fail-closed default
for notification, not a bug: an unrecognized event_type never silently notifies
anyone.