Email the team member when an Inbox item is assigned to them
Status: approved by founder 2026-10-22 ("yes, great idea...go for all the suggested changes").
Surface: church admin dashboard Inbox (assign control → POST /api/inbox/assign), Team members settings. Church tenants first; vertical tenants (funeral/vet/realtor/local business) use the same assign route and get the same behaviour only if it already reaches them through the same code path; no new vertical-specific copy.
Today
Assigning only writes inbox_item_state.assigned_to (via RPC). Nobody is told. Volunteers who never open the dashboard never learn an item is theirs.
Behaviour
- Setting per team member: "Email me when something is assigned to me". Default ON for every team member, existing and new.
- The member can change it for themselves; an admin (team-management capability) can change it for any member.
- Shown on the Team members list/edit form and on the member's own profile/notification preferences if one exists.
- When it sends: immediately after a successful assignment to a member whose setting is ON.
- Never when the assigner assigns to themselves.
- Never on unassign.
- Not again if the item is re-assigned to the same person it is already assigned to.
- A failure to send never fails the assignment; it is reported to
ops_errors(same pattern as other notification failures).
- Email content:
- Subject: "
: a was assigned to you" (type = prayer request, callback request, visitor, benevolence request, volunteer, message…; use the Inbox's existing type labels). - Body: who assigned it (first name), the person's first name only, a one-line summary, the item's date, and a button "Open in dashboard".
- Privacy: pastoral items (prayer, care, crisis/safety, benevolence) show only a short neutral line, e.g. "Prayer request from Mary — please follow up." Never the full prayer text, phone number, address, transcript or financial details in the email. Visitor and callback items get the same short neutral line as pastoral items; no summary of the person's free text is ever included, because it can hold addresses and health detail.
- Footer: "You get this because you're on
's team. Turn these emails off: ."
- Subject: "
- The link opens the item in the dashboard using the member's own sign-in (their existing login / magic-link flow). It must never contain the church's admin token or any long-lived secret. If the member isn't signed in they go through the normal sign-in and land on the item.
- Sender: the church's configured email sender settings (same as other church notification emails); reply-to follows the existing inbox reply-to rules from #1861.
- Respect RBAC: only send if the assignee can actually see that item type (the assign route already validates membership; also check the item type is visible to their role, e.g. a benevolence item only to BENEVOLENCE_ROLES). If not, skip the email and log it.
Out of scope
SMS on assign, daily digests, per-type preferences, reminders for overdue items.
Acceptance checks
- Unit/contract tests: sends on assign-to-other with setting ON; no send when self-assigned, unassigned, same-assignee repeat, setting OFF, or assignee can't see the type; email body for pastoral types contains no full text/phone/address; link contains no admin token.
- Migration (if a column is needed) is additive with default true, reviewed and applied only with founder approval.
- Deployed check on the demo church: assign an item to a demo team member whose email is a test inbox we control; the email arrives with the right content and the link opens the item after sign-in. Never send to a real church member during testing.
Implementation notes (2026-10-22, PR #1882)
- Link (founder-approved 2026-10-22: 7-day personal sign-in link). The button points at
/auth/member?t=<token>. Token is random, only its HMAC hash is stored ininbox_member_links(migration2026-10-22b_inbox_member_links.sql, additive, needs founder approval to apply; until applied the email falls back to a plain/admin?tab=…link). GET only shows a confirm button; POST redeems once (atomicused_at IS NULL AND expires_at > now()), creates/finds the member'schurch_admin_identitiesrow + exactly onechurch_identity_rolesrow (their own role), mints a normalchurch_admin_sessionsrow (7-day expiry) and redirects to a clean URL. Refused (without burning the link) for inactive, role-less, email-less members and members with custom groups or direct capabilities (a role-only session would misstate their real permissions). Used/expired links show a page with "Email me a new link", sent only to the member's own address on file. Mint cap 6/member/hour. - Landing: the Inbox filtered to the item's type (chip deep link). A per-item deep link does not exist in the dashboard yet.
- Setting:
tenant_team_members.notify_on_assign(applied). Admin toggle in Settings > Team (AssignEmailToggle); members can also switch off from the email footer (single-purpose signed token,/api/inbox/assign-email/off). - Access safety (QA 2026-10-22): the link flow never touches owner or backup-owner identities (refused before the token is consumed, 'use your owner sign-in' page); refuses when an existing identity's roles differ from exactly {member role}; reactivates an inactive non-owner identity only together with writing the role (role first, then activate). Removing/deactivating a member revokes that identity's sessions, marks it inactive and deletes its roles for that church, fail closed; reactivating a member does not touch the identity (the next link restores it safely). The three token-in-body POSTs require same-origin (login-CSRF guard). The opt-out token is keyed off APP_SECRET.
- Known limits: landing is the Inbox filtered to the item type (no per-item deep link yet); redeeming creates a
church_admin_identities/church_identity_rolesrow for members who had none; owners who are also team members must use their owner sign-in.