feat: add resend capability for platform and account invite codes
Revoke-and-recreate flow for both invite systems with email delivery via Resend API. Includes account invite email template and audit logging. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
74
docs/plans/2026-02-12-resend-invite-codes.md
Normal file
74
docs/plans/2026-02-12-resend-invite-codes.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Resend Invite Codes — Design
|
||||
|
||||
**Date**: 2026-02-12
|
||||
|
||||
## Summary
|
||||
|
||||
Add a "Resend" capability to both invite systems (platform invite codes and account invites). Resending revokes the old code and generates a fresh one, then emails it to the recipient.
|
||||
|
||||
## Backend
|
||||
|
||||
### Platform Invite Codes
|
||||
|
||||
**New endpoint**: `POST /api/v1/invites/{code}/resend` (admin-only)
|
||||
|
||||
1. Look up existing invite by code
|
||||
2. Reject if already used (409 Conflict)
|
||||
3. Delete the old invite
|
||||
4. Create a new invite with the same properties (email, plan, trial_days, note, expiration recalculated from now)
|
||||
5. Send email via `EmailService.send_invite_email()`
|
||||
6. Log audit event
|
||||
7. Return the new invite
|
||||
|
||||
### Account Invites
|
||||
|
||||
**New endpoint**: `POST /api/v1/accounts/me/invites/{invite_id}/resend` (owner-only)
|
||||
|
||||
1. Look up existing invite by ID
|
||||
2. Reject if already used (409 Conflict)
|
||||
3. Delete the old invite
|
||||
4. Create a new invite with the same email, role, and fresh expiration
|
||||
5. Send email via new `EmailService.send_account_invite_email()`
|
||||
6. Log audit event
|
||||
7. Return the new invite
|
||||
|
||||
### New Email Method: `send_account_invite_email()`
|
||||
|
||||
Added to `EmailService` in `backend/app/core/email.py`.
|
||||
|
||||
- **Parameters**: `to_email`, `code`, `account_name`, `role`, `signup_url`
|
||||
- **Subject**: "You've been invited to join [Account Name] on ResolutionFlow"
|
||||
- **Body**: Same dark monochrome HTML template as platform invites, with:
|
||||
- "You've been invited to join **[Account Name]** as an **Engineer/Viewer**"
|
||||
- Prominent invite code display (same style)
|
||||
- No plan/trial section
|
||||
- "Create Your Account" CTA button
|
||||
- **Returns** `bool` — best-effort, never raises
|
||||
|
||||
## Frontend
|
||||
|
||||
### Platform Invite Codes (`InviteCodesPage.tsx`)
|
||||
|
||||
- Resend button in actions column (next to copy/delete)
|
||||
- Only visible for unused, non-expired codes with an email address
|
||||
- Calls `POST /api/v1/invites/{code}/resend`
|
||||
- Shows success toast with new code, refreshes list
|
||||
- Loading state on button during API call
|
||||
|
||||
### Account Invites (`AccountSettingsPage.tsx`)
|
||||
|
||||
- Resend button next to each pending invite
|
||||
- Only visible for unused, non-expired invites
|
||||
- Calls `POST /api/v1/accounts/me/invites/{invite_id}/resend`
|
||||
- Shows success toast, refreshes list
|
||||
- Loading state on button during API call
|
||||
|
||||
## Files to Modify
|
||||
|
||||
- `backend/app/core/email.py` — add `send_account_invite_email()` + HTML template
|
||||
- `backend/app/api/endpoints/invite.py` — add resend endpoint for platform codes
|
||||
- `backend/app/api/endpoints/accounts.py` — add resend endpoint for account invites
|
||||
- `frontend/src/pages/admin/InviteCodesPage.tsx` — add resend button
|
||||
- `frontend/src/pages/AccountSettingsPage.tsx` — add resend button
|
||||
- `frontend/src/api/admin.ts` — add resend API call
|
||||
- `frontend/src/api/accounts.ts` — add resend API call
|
||||
Reference in New Issue
Block a user