Invitation email via ak_send_email, not NotificationTransport
Date: 2026-06-26 Status: Accepted Decision: hl-a1uh Deciders: sean
Context
Section titled “Context”Authentik has two structurally separate outbound-email paths: a NotificationTransport (mode=email) delivers only to existing Authentik users via notification.user.email, while the ak_send_email() expression function accepts arbitrary recipient addresses. When an invitation is created, the invitee has no user account yet, and the invitation_created event’s user field is the admin who created the invitation — not the invitee.
Decision
Section titled “Decision”Invitation email is sent via ak_send_email() inside an Authentik expression policy (tf/authentik/policies/send_invitation_email.py), not via a native email NotificationTransport. The native transport is structurally incapable of reaching a pre-registration address.
Rationale
Section titled “Rationale”- NotificationTransport selects recipients from
notification.user.email— only existing registered users. - The
invitation_createdevent’s user is the inviting admin, not the invitee; a native transport would email the wrong person. ak_send_email(address=invitee_email, ...)accepts caller-supplied addresses and reuses the same globalAUTHENTIK_EMAILSMTP config.- This constraint is permanent for as long as invitations pre-date user-account creation.
Alternatives Considered
Section titled “Alternatives Considered”ak_send_email()in an expression policy (chosen): accepts arbitrary addresses; reuses the global SMTP config; async via the Authentik task queue; failures observable in event/task logs instead of a silentpass.- Native NotificationTransport (mode=email) (rejected): clean IaC resource and consistent with the other notification rules, but can only deliver to existing users — it would email the inviting admin, not the invitee. Structurally impossible to reach a pre-registration address.
Consequences
Section titled “Consequences”- Positive: invitation email reaches pre-registration addresses;
ak_send_emailis async and does not block the policy thread during invitation creation; mail failures surface in Authentik task logs and ClickStack instead of being silently swallowed. - Negative: two outbound-email paths (NotificationTransport and
ak_send_email) must be understood and maintained separately; theinvitation_email_dummy(mode=local) transport cannot be removed because event rules require at least one transport, so it remains a permanent structural placeholder. - Neutral:
ak_send_emailreturns True when the mail is queued, not when delivered; delivery confirmation is not available at call time.