Guides

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.

What system SMTP is actually used for. Three things: user invitation emails, password resets, and a fallback path so a notification still reaches somebody when no email channel was resolved for it. That last one is per-event, not per- organization — see what goes where below, and do not assume adding an email channel switches it off.

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.

Check 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:

TypeYou must supplyAlso available
EmailSMTP host, SMTP port, SMTP password, From addressSMTP user, TLS, From name
SlackWebhook URLChannel override, mention list
DiscordDiscord webhook URLOverride username and avatar
TelegramBot token, Chat ID
Microsoft TeamsTeams webhook URL
WebhookURL, SecretTimeout in seconds
PagerDutyIntegration routing key, default severity, at least one trigger
ServiceNowInstance URL, username, password, at least one triggerAssignment group, urgency, two-way sync
JiraSite URL, account email, API token, project key, at least one triggerIssue type, two-way sync
An email channel needs its own SMTP password. It does not inherit the system SMTP settings, and leaving its fields blank does not fall back to them — an email channel is a full, independent SMTP configuration.

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.
Binding channels to a review plan has no form field yet. The review plan carries the list, and the REST API, Terraform provider and bootstrap all accept it, but the review-plan screen does not render an input for it. Until it does, set it through one of those three — Terraform is the least painful if you are already declaring plans there.

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.

Verify the signature over the raw bytes. Compute the HMAC before your framework parses, re-serialises or pretty-prints the body — any of those changes the bytes and the signature will not match. Compare in constant time.

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.