Transactional email via Resend for auth flows, workflow approvals, and analysis delivery.
What Sends Email
| Trigger | Source | Description |
|---|---|---|
| Email verification | lib/mails/auth-emails.ts | Sent on signup (OTP code) |
| Magic link login | lib/mails/auth-emails.ts | Passwordless sign-in link |
| Team invitation | lib/mails/auth-emails.ts | Invite a user to your team |
| Workflow plan approval | lib/ai/workflows/emails.ts | Sent after a workflow is created (draft state) |
| Workflow step approval | lib/ai/workflows/emails.ts | Sent when a step requires human review |
| Workflow completed | lib/ai/workflows/emails.ts | Run finished successfully |
| Workflow failed | lib/ai/workflows/emails.ts | Run hit an unrecoverable error |
| Legal analysis delivery | send-analysis-email.ts tool | Sent by the legal-analyzer agent |
Workflow Email Functions
// lib/ai/workflows/emails.ts
// Plan approval — sent immediately after workflow creation
sendPlanApprovalEmail({
to: string,
workflowId: string,
workflowTitle: string,
goal: string,
steps: WorkflowStep[],
token: string, // 24h JWT
})
// Step approval — sent when a step has needsApproval: true
sendStepApprovalEmail({
to: string,
workflowId: string,
runNumber: number,
stepDescription: string,
approvalPrompt: string,
toolOutput: string, // preview of what the tool produced
token: string, // 2h JWT
})
// Run finished
sendRunCompletedEmail({ to, workflowTitle, workflowId, runNumber })
// Run failed
sendRunFailedEmail({ to, workflowTitle, workflowId, runNumber, stepDescription, error })Approval Links
Approval emails include one-time JWT links:
| Link type | Route | Expiry |
|---|---|---|
| Plan approval | GET /api/workflows/[id]/approve?token=... | 24 hours |
| Step approve | GET /api/workflows/resume?token=...&action=approve | 2 hours |
| Step reject | GET /api/workflows/resume?token=...&action=reject¬e=... | 2 hours |
Tokens are HS256 JWTs signed with WORKFLOW_TOKEN_SECRET using the jose library.
Bot-Prefetch Guard
Email clients (Outlook, Gmail) send a prefetch GET to any link in an email to generate a preview. Without protection, this would trigger workflow approvals before the user even opens the email.
GET /api/workflows/resume checks the User-Agent header and returns a neutral 200 OK (no side effects) for known bot/crawler user agents. Only requests with a human browser UA actually execute the approval.
Required Environment Variables
RESEND_API_KEY=re_...
EMAIL_FROM=Ship AI SaaS <noreply@yourdomain.com>You must verify your sending domain in the Resend dashboard before emails will deliver in production. The noreply@yourdomain.com address must be on a verified domain. See the Resend docs for the verification steps.