Set up email and chat notifications.
Last updated
What you are building
Two different things share one page, and it saves confusion to separate them up front:
- System SMTP — the mail server AccessFlow itself uses. Without it, user invitations and password resets do not work at all.
- Notification channels — where review and audit events are announced: email, Slack, Discord, Telegram, Microsoft Teams, PagerDuty, ServiceNow, Jira, or a signed webhook.
Both live at Notifications in the admin section. Set up SMTP first — several other things depend on it.
Do I have to configure email to use AccessFlow?
No, but two features stop working without it: inviting a user by email, and password reset. Both fail with a clear error rather than silently doing nothing. You can run AccessFlow perfectly well with no mail at all if you create accounts with passwords and use single sign-on.
1. System SMTP
The System SMTP card sits at the top of the Notifications page. Choose Configure and fill in Host, Port, From address, and the password if your provider needs one. Username and From display name are optional, and STARTTLS is on by default.
Use Send test before you move on. It takes an optional recipient and defaults to the from-address, and it tells you whether the server accepted the message — which is a far quicker signal than discovering it later through a failed invitation.
Passwords are stored encrypted and never returned; the form shows a masked value, and leaving it untouched keeps the stored one.
To declare it rather than click it, set
ACCESSFLOW_BOOTSTRAP_ENABLED=true and
ACCESSFLOW_BOOTSTRAP_SYSTEM_SMTP_ENABLED=true, then supply
_HOST, _PORT, _FROM_ADDRESS and the optional
_USERNAME, _PASSWORD, _TLS and
_FROM_NAME. Both switches are required: with the section flag unset,
bootstrap skips system SMTP silently — no error, and nothing configured.
Bootstrap is the source of truth for what it declares, but it does not re-write unchanged rows: it stores a fingerprint of each declared spec and skips any resource whose spec has not changed since the last run. So an edit you make in the admin UI survives a restart, and is only overwritten once you change the declared values themselves. Treat the declared set as authoritative anyway — anything else drifts.
ACCESSFLOW_PUBLIC_BASE_URL too. It is the origin
AccessFlow builds links from in the mail it sends. Its default points at a local
development frontend, so if you leave it alone, your invitation and notification links
will point somewhere your users cannot reach.
2. Add a channel
Add channel, give it a name, pick a type, and fill in what that type needs. The nine types and their required fields:
| Type | You must supply | Also available |
|---|---|---|
| SMTP host, SMTP port, SMTP password, From address | SMTP user, TLS, From name | |
| Slack | Webhook URL | Channel override, mention list |
| Discord | Discord webhook URL | Override username and avatar |
| Telegram | Bot token, Chat ID | — |
| Microsoft Teams | Teams webhook URL | — |
| Webhook | URL, Secret | Timeout in seconds |
| PagerDuty | Integration routing key, default severity, at least one trigger | — |
| ServiceNow | Instance URL, username, password, at least one trigger | Assignment group, urgency, two-way sync |
| Jira | Site URL, account email, API token, project key, at least one trigger | Issue type, two-way sync |
Every secret you enter — SMTP passwords, webhook secrets, bot tokens, PagerDuty routing keys, ServiceNow passwords, Jira API tokens — is encrypted before it is stored and replaced by a mask when read back. On edit, leaving the mask alone keeps the stored value; secrets are only required when you first create the channel.
Each channel has a Test button. For email channels it asks for an optional override recipient first. Test messages are deliberately kept out of the in-app inbox, so a successful test is quiet everywhere except the channel you are testing.
3. Understand what goes where
This is the part worth reading carefully, because it is not what most people assume. There is no per-channel event picker. Routing works two ways, depending on the event:
- Organization-wide events go to every active channel. High AI risk, a detected anomaly, a break-glass execution, the weekly digest, a recertification campaign opening, a stale grant, a sensitive result export, a failed connector token, and all five deployment events. Add a channel and it starts receiving these immediately.
- Everything else — the ordinary query lifecycle — follows the review plan. Those notifications go to the channels bound to the review plan of the datasource the query targeted.
PagerDuty and the two ticketing types are the exception: they carry their own trigger list, so you choose exactly which events raise an incident or open a ticket.
- PagerDuty — Trigger on: AI critical-risk query, review timeout, behavioural anomaly detected, break-glass query executed, routing-policy escalation, review stalled.
- ServiceNow and Jira — Create a ticket on: query rejected, review timeout, routing-policy escalation.
4. Webhooks, and verifying them
A webhook channel posts JSON to your URL with three headers:
X-AccessFlow-Event naming the event type,
X-AccessFlow-Delivery carrying a unique delivery id, and
X-AccessFlow-Signature holding
sha256=<hex> — an HMAC-SHA256 of the raw request body, keyed by the
secret you configured.
Delivery is one attempt plus up to three retries, after roughly 30 seconds, 2 minutes
and 10 minutes. Tune them with
ACCESSFLOW_NOTIFICATIONS_RETRY_FIRST, _SECOND and
_THIRD. When all four attempts fail the exhaustion is recorded in the audit
log with the attempt count and the last HTTP status, so a quietly broken endpoint is
discoverable rather than invisible. A channel deleted or deactivated mid-retry is not
retried again.
5. Two-way sync with ServiceNow or Jira
A ticketing channel opens a ticket when one of its triggers fires. Switch on Bi-directional status sync and the reverse also works: resolving the ticket in ServiceNow or Jira approves or rejects the waiting query in AccessFlow.
Enabling it makes Webhook secret mandatory — the inbound endpoint is
unauthenticated apart from the signature, so without a secret there is nothing to trust.
Point your ticketing system's outbound webhook at the per-channel URL under
/api/v1/integrations/servicenow/webhook/ or
/api/v1/integrations/jira/webhook/, and sign each request with
X-AccessFlow-Timestamp and X-AccessFlow-Signature. Requests
older than five minutes are rejected as replays; adjust with
ACCESSFLOW_NOTIFICATIONS_TICKETING_SIGNATURE_TOLERANCE.
Which resolutions count is configurable per channel. Out of the box, statuses like resolved, closed, done, approved and complete approve; rejected, declined and cancelled reject. Matching is case-insensitive, and a decision already made in AccessFlow wins — a late ticket resolution cannot overturn it.
Channel-by-channel field reference, message formats and the full event list: Notifications.