MailGap Reply User Flow
Status: Draft Last Updated: 2026-02-06 Owner: Product / Engineering
Purpose
This document defines the user flow for replying to an inbound email within the MailGap Proxy Email Service.
It describes:
- How users initiate a reply
- When replies are allowed or blocked
- Page-by-page UI layout
- System checks and enforcement points
This document defines how users experience replies. All rules are enforced server-side per the MailGap specification and contracts.
Scope
In Scope
- Replying to an inbound MailGap message
- Credit checks and consumption
- Abuse and suppression enforcement
- Blocked reply states
Out of Scope
- Composing new outbound emails
- Forwarding emails
- Attachments in replies
- HTML email composition
Preconditions
The flow assumes:
- User is authenticated
- User owns the MailGap alias
- Inbound message exists
- Alias status is active
- User has not exceeded daily limits
Authorization MUST comply with:
- docs/contracts/authz-and-roles.new.md
- docs/contracts/rls-standard.new.md
Entry Points
Users enter this flow from:
MailGap Inbox → Message Detail View
- CTA: “Reply”
Notification deep link (push, email, or in-app alert)
- Route: /mailgap/messages/[message_id]
- Lands directly on Message Detail View
- Requires authentication
- Requires ownership validation
Deep Link Authentication Behavior
If the user taps a notification while logged out:
- Redirect to login
- After successful login, redirect back to /mailgap/messages/[message_id]
- Validate message ownership via RLS
- If ownership fails, show 404 or generic error
This behavior MUST be implemented consistently for all notification types.
High-Level Flow Overview (ASCII)
[ Notification or Inbox ] | v [ Message Detail View ] | v [ Reply Composer ] | +--> [ Reply Blocked ] | v [ Send Attempt ] | +--> [ Error State ] | v [ Reply Sent Success ]
Page-by-Page Flow
Page 1: Message Detail View
Route: /mailgap/messages/[message_id]
Purpose
Allow the user to read an inbound message and initiate a reply.
UI Layout (ASCII)
+--------------------------------------------------+ | Header | | - MailGap | | - Credits: 72 / 100 | +--------------------------------------------------+ | | | From: support@example.com | | To: alias@cairlmail.com | | Subject: Your request |
| Message body (plain text) |
| ------------------------------------------------ |
| [ Reply ] [ Delete ] |
| +--------------------------------------------------+ |
UI Elements
- Message metadata (from, subject)
- Message body (plain text)
- Reply CTA
- Credit indicator
User Actions
| Action | Result |
|---|---|
| Click Reply | Proceed to Reply Composer |
| Click Delete | Message removed |
System Behavior
On load:
- Authenticate user
- Validate message ownership via RLS
- Check alias status
- Determine reply eligibility
If ownership validation fails:
- Do not reveal message existence
- Return 404 or generic error
Transition Conditions
| Condition | Next Page |
|---|---|
| Reply allowed | Reply Composer |
| Reply blocked | Reply Blocked State |
Page 2: Reply Composer
Route: /mailgap/messages/[message_id]/reply
Purpose
Allow the user to compose a plain-text reply.
UI Layout (ASCII)
+--------------------------------------------------+ | Reply to: support@example.com | | From: alias@cairlmail.com | +--------------------------------------------------+ | | | +----------------------------------------------+ | | | Type your reply (plain text only) | | | | | | | | | | | +----------------------------------------------+ | | | | Credits required: 10 | | Credits remaining: 72 | | | | [ Send Reply ] [ Cancel ] | | | +--------------------------------------------------+
User Actions
| Action | Result |
|---|---|
| Click Send Reply | Attempt send |
| Click Cancel | Return to Message Detail |
System Behavior (on Send)
Server performs, in order:
- Ownership check (RLS)
- Alias status check
- Allowlist validation
- Suppression check
- Credit availability check
- Abuse guardrail checks
- Send via email provider
- Persist outbound message
- Deduct credits atomically
Failures short-circuit immediately.
Transition Conditions
| Condition | Next Page |
|---|---|
| Send success | Reply Sent Success |
| Any failure | Error or Blocked State |
Blocked and Error States
Blocked State: Reply Not Allowed
Displayed when:
- Sender is not allowlisted
- Recipient is suppressed
- Alias is paused or disabled
- Safety guardrails block sending
UI Layout (ASCII)
+--------------------------------------------------+ | ❌ Reply Not Available | | | | This sender cannot be replied to at this time. | | | | Reason (generic, non-exploitable): | | “Replies are restricted to protect deliverability”| | | | [ Request Access ] (if applicable) | | [ Back to Inbox ] | +--------------------------------------------------+
Behavior:
- No send attempt occurs
- Credits are not consumed
- Event is logged
Error State: Send Failed
Displayed when:
- Temporary provider failure
- Network or system error
UI Layout (ASCII)
+--------------------------------------------------+ | ⚠️ Unable to Send Reply | | | | Something went wrong. Please try again later. | | | | [ Retry ] [ Cancel ] | +--------------------------------------------------+
Behavior:
- Credits not consumed
- Retry allowed
- Error logged for observability
Success State
Reply Sent Confirmation
Purpose
Confirm the reply was successfully sent.
UI Layout (ASCII)
+--------------------------------------------------+ | ✅ Reply Sent | | | | Your message has been sent successfully. | | | | [ Back to Message ] | | [ Go to Inbox ] | +--------------------------------------------------+
Error Handling Summary
| Error Type | User Message | Recovery |
|---|---|---|
| Allowlist | Reply not available | Request access |
| Suppression | Cannot reply | None |
| Rate limit | Try again later | Wait |
| Credits | Not enough credits | Upgrade |
| System | Temporary issue | Retry |
Observability Notes
This flow MUST emit:
- Reply attempt events
- Blocked reply events (with reason code)
- Successful send events
- Provider failure events
- Deep-link login redirects
Observability MUST comply with:
- docs/architecture/observability-plan.new.md
Non-Negotiable Rules
- Replies are reply-only
- UI gating is advisory only
- Server enforces all checks
- Credits deducted atomically
- Blocked states do not leak abuse logic
Acceptance Criteria
- Deep links land on Message Detail View
- Logged-out users are redirected back after login
- Ownership validation enforced on load
- Reply behavior matches spec
- Credits and abuse controls enforced
- All events observable
References
- docs/specs/mailgap/mailgap-proxy-email-service.spec.new.md
- docs/contracts/authz-and-roles.new.md
- docs/contracts/rls-standard.new.md
- docs/contracts/rate-limits-and-abuse.new.md
- docs/architecture/observability-plan.new.md
End of Document