Skip to main content
VulnTrack uses Resend for reliable transactional email delivery. Email functionality is required for team invitations, password resets, and vulnerability assignment notifications.

Email Types

VulnTrack sends three types of automated emails:

Invitations

Secure invitation links for new team members with 24-hour expiration.

Password Resets

Time-limited password reset links with 1-hour expiration.

Assignments

Notifications when vulnerabilities are assigned to analysts.

Setup with Resend

1

Create Resend account

Sign up at resend.com and verify your email.
2

Add and verify domain

Navigate to Domains in the Resend dashboard:
  1. Click Add Domain
  2. Enter your domain (e.g., vulntrack.yourdomain.com)
  3. Add the provided DNS records to your domain:
    • SPF record for sender authentication
    • DKIM records for email signing
    • MX record (optional, for receiving replies)
DNS propagation can take up to 48 hours. Use Resend’s verification tool to check status.
3

Generate API key

In the Resend dashboard:
  1. Go to API Keys
  2. Click Create API Key
  3. Name it (e.g., “VulnTrack Production”)
  4. Copy the key immediately (it won’t be shown again)
4

Configure environment variables

Add to your .env file:
.env
RESEND_API_KEY="re_your_api_key_here"
EMAIL_FROM="VulnTrack <[email protected]>"
The email address in EMAIL_FROM must use your verified domain.

Environment Variables

RESEND_API_KEY
string
Your Resend API key for sending emails.Format: re_xxxxxxxxxxxxGet from: Resend Dashboard > API KeysIf not set, VulnTrack will operate in mock mode and log emails to console instead of sending them.
EMAIL_FROM
string
default:"VulnTrack System <[email protected]>"
Sender email address displayed in outgoing emails.Format: Display Name <[email protected]>Examples:
Must match a verified domain in your Resend account.
NEXT_PUBLIC_APP_URL
string
required
Base URL used in email links.Development: http://localhost:3000Production: https://vulntrack.yourdomain.comThis URL is prepended to invitation and password reset links.

Email Templates

VulnTrack uses custom HTML email templates defined in src/lib/email-templates.ts.

Invitation Email

Triggered when: Admin sends a team invitation Contains:
  • Personalized greeting
  • Invitation acceptance button
  • Link expiration warning (24 hours)
  • Direct link fallback
Implementation:
src/lib/email-templates.ts:67
export function getInvitationEmail(inviteLink: string)

Password Reset Email

Triggered when: User requests password reset Contains:
  • Reset password button
  • Security notice
  • Link expiration warning (1 hour)
  • Ignore instructions for unsolicited resets
Implementation:
src/lib/email-templates.ts:95
export function getPasswordResetEmail(resetLink: string)

Assignment Notification

Triggered when: Vulnerability is assigned to a user Contains:
  • Vulnerability title and ID
  • Direct link to vulnerability details
  • Call to action for remediation
Implementation:
src/lib/email-templates.ts:123
export function getAssignmentEmail(vulnerabilityTitle: string, vulnerabilityId: string)

Email Service (src/lib/email.ts)

The core email sending function:
src/lib/email.ts:17
export async function sendEmail({
  to: string,
  subject: string,
  html: string,
  text?: string
})

Mock Mode

When RESEND_API_KEY is not set, emails are logged to console:
[MOCK EMAIL] To: [email protected], Subject: Welcome to VulnTrack
[MOCK EMAIL] HTML Preview: <div style="font-family: 'Segoe UI'...
Use cases:
  • Local development without Resend account
  • Testing email triggers
  • CI/CD environments

Production Mode

With RESEND_API_KEY configured, emails are sent via Resend:
const data = await resend.emails.send({
  from: FROM_EMAIL,
  to,
  subject,
  html,
  text,
})

Sending Emails from Code

Example: Send invitation

import { sendEmail } from "@/lib/email"
import { getInvitationEmail } from "@/lib/email-templates"

const inviteLink = `${process.env.NEXT_PUBLIC_APP_URL}/accept-invite?token=${token}`
const html = getInvitationEmail(inviteLink)

await sendEmail({
  to: "[email protected]",
  subject: "You're invited to join VulnTrack",
  html,
})

Example: Send password reset

import { sendEmail } from "@/lib/email"
import { getPasswordResetEmail } from "@/lib/email-templates"

const resetLink = `${process.env.NEXT_PUBLIC_APP_URL}/reset-password?token=${resetToken}`
const html = getPasswordResetEmail(resetLink)

await sendEmail({
  to: user.email,
  subject: "Reset your VulnTrack password",
  html,
})

Example: Send assignment notification

import { sendEmail } from "@/lib/email"
import { getAssignmentEmail } from "@/lib/email-templates"

const html = getAssignmentEmail(vulnerability.title, vulnerability.id)

await sendEmail({
  to: assignee.email,
  subject: `New Assignment: ${vulnerability.title}`,
  html,
})

Template Customization

Email templates use inline CSS for maximum email client compatibility.

Shared Styles (src/lib/email-templates.ts:1)

export const styles = {
  container: `..`,
  header: `..`,
  button: `..`,
  footer: `..`,
}

Customization Options

1

Update branding

Modify the logo and color scheme in styles.logo and styles.button.
2

Change sender name

Update EMAIL_FROM environment variable.
3

Modify footer

Edit commonFooter constant in email-templates.ts:60.
4

Add new email type

Create a new function in email-templates.ts following the existing pattern.

Testing Emails

Local Testing (Mock Mode)

# Run without RESEND_API_KEY
npm run dev

# Trigger an email action (e.g., send invitation)
# Check console for output:
[MOCK EMAIL] To: [email protected], Subject: ...

Production Testing

1

Use Resend test mode

Send emails to your own verified address first.
2

Check Resend logs

View delivery status in Resend Dashboard > Logs.
3

Test all templates

Manually trigger each email type:
  • Send a test invitation
  • Request a password reset
  • Assign a vulnerability
4

Verify deliverability

Check spam folders and email headers (SPF/DKIM/DMARC).

Troubleshooting

Symptoms: No emails received, no errors in consoleSolutions:
  • Verify RESEND_API_KEY is set correctly
  • Check Resend dashboard for API errors
  • Ensure domain is verified in Resend
  • Check rate limits (Resend free tier: 100 emails/day)
Symptoms: Domain not verified errorSolutions:
  • Wait 24-48 hours for DNS propagation
  • Use Resend’s verification tool to check DNS records
  • Ensure DNS records match exactly (no trailing dots or spaces)
  • Try using dig or nslookup to verify DNS changes
Symptoms: Emails delivered but marked as spamSolutions:
  • Verify SPF, DKIM, and DMARC records are configured
  • Use a professional sender address (not gmail.com)
  • Add plain text version to emails
  • Warm up your domain (send gradually increasing volumes)
Symptoms: 403 Forbidden or Invalid from addressSolutions:
  • Ensure EMAIL_FROM uses your verified Resend domain
  • Check for typos in email address
  • Verify domain verification status in Resend

Rate Limits

Resend free tier limits:
  • 100 emails per day
  • 3,000 emails per month
For production deployments with higher volume, upgrade to a paid plan.
Monitor usage in Resend Dashboard > Usage to avoid hitting limits.

Security Considerations

Secure API keys

Never commit RESEND_API_KEY to version control. Use environment variables or secrets management.

Time-limited tokens

Invitation and reset tokens expire automatically (24h and 1h respectively).

Single-use tokens

Tokens are consumed after first use to prevent replay attacks.

Verify senders

Always use verified domains to prevent spoofing and improve deliverability.
Production Warning: Never use unverified domains or [email protected] in production. This will result in poor deliverability and potential email rejection.

Build docs developers (and LLMs) love