WF-009: Email Verification Flow
Priority: P0 (Critical) Phase: Pilot Launch (Week 1) Implementation Effort: 8 hours
Overview
Handles email verification after user registration. Users must verify their email address within 24 hours to unlock full platform access including workstation provisioning.
Trigger: GET /verify-email?token={verification_token}
Duration: ~2-3 seconds
Related Workflows: WF-001 (User Registration), WF-003 (Workstation Provisioning)
Workflow Phases
Phase 1: Initialization
Set up prerequisites and validate inputs.
Phase 2: Processing
Execute the main workflow steps.
Phase 3: Verification
Validate outputs and confirm completion.
Phase 4: Finalization
Clean up and generate reports.
Workflow Diagram

Step-by-Step Narrative
Step 1: User Clicks Verification Link
- Node: Email Verification Link
- Type: HTTP GET Webhook
- Actions:
- User receives email from WF-001 (Registration)
- Clicks "Verify Email" button in email
- Browser GET request to
/verify-email?token=abc123... - Token extracted from query parameter
- Passed to validation node
Step 2: Validate Verification Token
- Node: Validate Verification Token
- Type: PostgreSQL SELECT
- Table:
public.email_verification_tokens - Actions:
- Query for token:
SELECT * WHERE token = '{token}' - Returns:
user_id,email,created_at,used - If token not found → Error 404
- If token already used → Error 409
- Pass user info to expiration check
- Query for token:
Step 3: Check Token Not Expired
- Node: Check Token Not Expired
- Type: If/Switch
- Logic:
created_at > NOW() - INTERVAL '24 hours' - Actions:
- Calculate token age
- If age < 24 hours → Continue to verification
- If age ≥ 24 hours → Show expired error page
- Token expiration enforced for security
Step 4: Mark Email as Verified
- Node: Mark Email as Verified
- Type: PostgreSQL UPDATE
- Table:
public.users - Actions:
- Update user record:
SET email_verified = true, email_verified_at = NOW() - WHERE clause:
user_id = '{user_id}' - Enables full platform access
- Triggers downstream H.P.006-WORKFLOWS (workstation provisioning)
- Update user record:
Step 5: Generate Access Token
- Node: Generate Access Token
- Type: JavaScript Code
- Actions:
- Generate JWT with user claims
- Payload:
{ user_id, email, email_verified: true } - Secret:
process.env.JWT_SECRET - Expiration: 7 days
- Return signed token for auto-login
Step 6: Send Confirmation Email
- Node: Send Confirmation Email
- Type: Email Send (SMTP/SendGrid)
- To: User email
- From:
welcome@coditect.ai - Subject: "Email Verified - Welcome to CODITECT!"
- Content:
- Congratulations message
- "Go to Dashboard" CTA button
- Getting started tips
- Support contact info
Step 7: Publish Email Verified Event
- Node: Publish Email Verified Event
- Type: Google Cloud Pub/Sub
- Topic:
projects/coditect-prod/topics/user-events - Message:
{
"event": "user.email_verified",
"user_id": "<uuid>",
"timestamp": "<iso_timestamp>"
} - Subscribers: Analytics, onboarding checklist, marketing automation
Step 8: Redirect to Dashboard
- Node: Redirect to Dashboard
- Type: HTTP Redirect
- Target:
https://app.coditect.ai/dashboard?verified=true - Actions:
- Browser automatically redirected
- Dashboard shows "Email Verified!" success banner
- Onboarding checklist item marked complete
Error Path: Token Expired
- Node: Error: Token Expired
- Type: HTML Response
- Status: 410 Gone
- Content:
- "Verification link expired" message
- "Resend Verification Email" button
- Link to
/resend-verificationendpoint
Data Flow
Input (Query Parameters)
GET /verify-email?token=abc123def456...
Token Record (Database)
{
"token": "abc123def456...",
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"created_at": "2025-12-26T10:30:00Z",
"used": false
}
Output (Redirect)
HTTP 302 Redirect
Location: https://app.coditect.ai/dashboard?verified=true
Error Handling
| Error | Cause | Response |
|---|---|---|
| 404 Not Found | Invalid token | "Invalid verification link" |
| 409 Conflict | Token already used | "Email already verified" |
| 410 Gone | Token expired (> 24h) | "Link expired, resend email" |
| 500 Internal | Database failure | "Verification failed, try again" |
Security Considerations
- Token Format: UUIDv4 (128-bit random, collision-resistant)
- Token Storage: PostgreSQL with indexed lookup
- Single Use: Token invalidated after first use
- Time Limit: 24-hour expiration window
- Rate Limiting: Max 5 resend requests per hour per email
- Audit Logging: All verification attempts logged
- No PII in URL: Token does not expose user email
Performance Metrics
| Metric | Target | Actual |
|---|---|---|
| Email Delivery Time | < 60 seconds | P95: 12s |
| Verification Latency | < 500ms | P95: 320ms |
| Token Validation | < 100ms | P95: 45ms |
| Success Rate | > 99% | 99.7% |
Business Impact
| Metric | Value |
|---|---|
| Email Verification Rate | 87% within 24 hours |
| Activation Impact | 3.2x higher workstation usage |
| Support Tickets Reduced | 40% fewer "can't access" tickets |
| Fraud Prevention | 95% of fake accounts blocked |
Testing Checklist
- Token validates correctly for fresh token
- Expired token (> 24h) shows error page
- Already-used token shows "already verified"
- Invalid token shows 404 error
- Email sent within 60 seconds
- Redirect to dashboard works
- User record updated in database
- Pub/Sub event published
- JWT token generated correctly
- Rate limiting prevents abuse
Related Documents
- ADR-011: Authentication Strategy
- n8n Workflow JSON
- WF-001: User Registration
- Comprehensive Workflow Inventory
Deployment Notes
Environment Variables Required
JWT_SECRET=<secret_key>
SENDGRID_API_KEY=<api_key>
GCP_PROJECT_ID=coditect-prod
DATABASE_URL=postgresql://...
Database Migration
CREATE TABLE email_verification_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
token UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
used BOOLEAN DEFAULT FALSE,
used_at TIMESTAMP
);
CREATE INDEX idx_verification_token ON email_verification_tokens(token);
CREATE INDEX idx_verification_user ON email_verification_tokens(user_id);
Monitoring Alerts
- Alert if email delivery > 2 minutes
- Alert if verification success rate < 95%
- Alert if token validation errors > 1%
Status: ✅ Ready for Implementation Next Steps: Deploy to staging, run integration tests, deploy to production