Skip to main content

Milestone 9: Invites and team

Purpose: Admins can invite users by email; invitee accepts (new user sets password, existing user gets membership); team settings list members and pending invites; audit log written and viewable.

Exit state: Invite flow (create + email); accept flow (new vs existing user); Settings → Team (members, invites, resend/revoke); Settings → Audit (table, filters).

Spec reference: §2.5 Invite users, §2.9 Audit log, §5.14 Invitation, §6.10 invite page, team settings, §10.4 Users & team members.

Prerequisites: M08 (Notifications — invite email uses Postmark).


9.1 Invitations

Tasks

  1. Create invite

    • tRPC: input email, role (admin/agent/viewer). Validate email format; check no existing membership for (tenant_id, email); check no pending invite for (tenant_id, email) or allow resend. Generate token (random, unique); set expires_at (e.g. now + 7 days). Insert invitations row; send invite email (Postmark, template from M08) with accept_url = APP_URL/invite/{token}. Section 2.5.
    • Audit: write audit_logs row action user.invited, resource invitation.
  2. Invite email

    • Template: org_name, inviter_name, role, accept_url, expires_at. Section 2.10b.
  3. Accept flow — route /invite/[token]

    • GET: validate token (exists, not expired, accepted_at null). Load invitation (tenant_id, email, role). If user not logged in: show "Sign in" or "Create account" with link that preserves token (e.g. /invite/[token] after login). If logged in: show "Accept" (add membership and redirect).
    • POST accept (or link click): validate token; if existing user (email matches): create tenant_memberships row; set invitation accepted_at; redirect to /app/[slug]/tickets (invited tenant slug). If new user: redirect to set-password page (same token or new token); after password set create user (password_hash set), create membership, set accepted_at; create session; redirect to /app/[slug]/tickets. Section 2.5, 6.10.
    • Set-password page for new user: use invite token to identify invitation; form new password (12 char min); on submit create user, membership, accept invite, session; redirect.
  4. Revoke invite

    • tRPC: set invitation accepted_at or delete; optionally send "invite revoked" (optional). Audit: user.invite_revoked.

Acceptance criteria

CriterionStatus
Admin can invite by email + role; invitee receives email with link.Pending
Existing user: accept adds membership and redirects to tenant.Pending
New user: accept leads to set password; after password, user created and membership added; redirect to tenant.Pending
Expired or invalid token shows clear message; revoke prevents accept.Pending
Audit log has user.invited and user.invite_revoked.Pending

9.2 Settings → Team

Tasks

  1. Members list

    • tRPC: list tenant_memberships for tenant with user (name, email, avatar_url). Display: name, email, role (select to change), Remove button. Role change: tRPC update membership role; audit user.role_changed. Remove: delete membership; optional reassign open tickets (Section 10.4); audit user.removed. Confirm Remove with AlertDialog.
  2. Invite member UI

    • "Invite member" button; modal: email input, role select; submit calls create invite (9.1). Show success "Invite sent."
  3. Pending invites

    • List invitations where accepted_at null and expires_at > now. Columns: email, role, expires_at, invited_by (optional). Actions: Resend (new token, resend email), Revoke. Section 6.10.

Acceptance criteria

CriterionStatus
Team table shows all members; Admin can change role and remove member; Remove prompts for reassign if needed.Pending
Invite modal sends invite; pending list shows with resend/revoke.Pending
Audit entries for role change and remove.Pending
Team list and pending invites reflected in real time when another admin adds/removes member or invite (SSE: e.g. team.updated or notification.new; Section 4.11).Pending

9.3 Audit log

Tasks

  1. Write audit events

    • Ensure all actions in Section 2.9 table write to audit_logs: tenant.updated, inbox., ticket.created (manual only), ticket.deleted, ticket.status_changed, ticket.assigned, ticket.moved, message.deleted, user.invited, user.invite_revoked, user.role_changed, user.removed, sla_policy., api_key., webhook., subscription.changed, tenant.deleted. Use user_id from context (null for system). resource_type, resource_id, metadata jsonb as needed.
  2. Settings → Audit

    • tRPC: list audit_logs for tenant; filters: date range, actor (user_id), action type. Sort by created_at desc. Table: timestamp, actor (name or "System"), action (human-readable), resource, optional metadata expandable. Section 6.10. Retention: do not delete in M09; pruning cron in M16 or document retention per plan (Section 2.9).
  3. Retention

    • Document or implement: Starter 30d, Growth 90d, Business 1y, Enterprise custom. Weekly cron to delete older rows (can be M10/M16).

Acceptance criteria

CriterionStatus
Key actions (invite, role change, remove, inbox connect, ticket create/delete, etc.) write to audit_logs.Pending
Settings → Audit shows entries; filter by date and actor works.Pending
Retention policy documented or cron in place.Pending

Milestone 9 sign-off

CriterionStatus
All tasks in 9.1–9.3 complete.Pending
All acceptance criteria met.Pending
Invite and accept flows work for new and existing users; team and audit visible to Admin.Pending
E2E: Invite send/accept and team/audit UI (see INDEX — Testing strategy).Pending
Ready for M10 (Billing and trial).Pending