XYLEX Group

email configuration

email configuration

this guide covers all configuration options for the email system

table of contents


environment variables

required

# Resend API Key
# Get from: https://resend.com/api-keys
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxxxx
```typescript

### optional

Sender Configuration

RESEND_FROM_EMAIL=noreply@suitsbooks.com RESEND_FROM_NAME=SuitsBooks RESEND_REPLY_TO=support@suitsbooks.com

Base URL for links in emails

NEXT_PUBLIC_BASE_URL=https://app.suitsbooks.com

Development Mode

Set to "true" to log emails instead of sending

EMAIL_TEST_MODE=false

Node environment

NODE_ENV=production


### environment-specific settings

Development (.env.local)

EMAIL_TEST_MODE=true NEXT_PUBLIC_BASE_URL=http://localhost:3000

Staging (.env.staging)

EMAIL_TEST_MODE=false NEXT_PUBLIC_BASE_URL=https://staging.suitsbooks.com

Production (.env.production)

EMAIL_TEST_MODE=false NEXT_PUBLIC_BASE_URL=https://app.suitsbooks.com


---

## email config

located in `lib/email/config.ts`:

```typescript
export const EMAIL_CONFIG = {
  // Default sender email (must be verified in Resend)
  defaultFrom: process.env.RESEND_FROM_EMAIL || "noreply@suitsbooks.com",

  // Default sender name
  defaultFromName: process.env.RESEND_FROM_NAME || "SuitsBooks",

  // Default reply-to address
  defaultReplyTo: process.env.RESEND_REPLY_TO || "support@suitsbooks.com",

  // Resend API key
  apiKey: process.env.RESEND_API_KEY || "",

  // Base URL for email links
  baseUrl: process.env.NEXT_PUBLIC_BASE_URL || "https://app.suitsbooks.com",

  // Is development environment
  isDevelopment: process.env.NODE_ENV === "development",

  // Test mode - logs emails instead of sending
  testMode: process.env.EMAIL_TEST_MODE === "true",

  // Environments where real emails are sent
  allowedEnvironments: ["production", "staging"],
};
```typescript

### customizing the config

to override these settings modify your `.env` file or update `lib/email/config.ts` directly

---

## template config

each template has associated configuration in `lib/email/config.ts`:

```typescript
export const TEMPLATE_CONFIG: Record<EmailTemplateId, TemplateConfiguration> = {
  invoice: {
    // Default subject (supports variables)
    defaultSubject: "Invoice from {{AUTHOR_NAME}}",
    
    // Whether authentication is required
    requiresAuth: true,
    
    // Category for grouping
    category: "transactional",
  },
  
  "password-reset": {
    defaultSubject: "Reset your password",
    requiresAuth: false,  // No auth needed for password reset
    category: "transactional",
  },
  
  welcome: {
    defaultSubject: "Welcome to SuitsBooks!",
    requiresAuth: false,  // No auth needed for welcome email
    category: "notification",
  },
  
  // ... more templates
};
```typescript

### template configuration options

| option | type | description |
|--------|------|-------------|
| `defaultsubject` | string | default subject line (supports variables) |
| `requiresauth` | boolean | whether auth headers are required |
| `category` | string | email category: "transactional" "marketing" "notification" |

### adding a new template config

```typescript
// In lib/email/config.ts
export const TEMPLATE_CONFIG = {
  // ... existing templates
  
  "my-new-template": {
    defaultSubject: "My New Template Subject",
    requiresAuth: true,
    category: "notification",
  },
};
```typescript

---

## rate limit config

default rate limits in `lib/email/config.ts`:

```typescript
export const DEFAULT_RATE_LIMITS = {
  // Maximum emails per minute (per user)
  maxEmailsPerMinute: 10,

  // Maximum emails per hour (per user)
  maxEmailsPerHour: 100,

  // Maximum emails per day (per user)
  maxEmailsPerDay: 500,

  // Duplicate detection window (seconds)
  duplicateWindowSeconds: 60,

  // Threshold for bulk confirmation
  bulkThreshold: 10,
};
```typescript

### customizing rate limits

#### per-hook customization

```typescript
const { sendEmail } = useSendEmail({
  maxEmailsPerMinute: 5,    // Stricter limit
  maxEmailsPerHour: 50,
  maxEmailsPerDay: 200,
});
```typescript

#### global customization

modify `default_rate_limits` in `lib/email/config.ts`:

```typescript
export const DEFAULT_RATE_LIMITS = {
  maxEmailsPerMinute: 20,   // Increased from 10
  maxEmailsPerHour: 200,    // Increased from 100
  maxEmailsPerDay: 1000,    // Increased from 500
  // ...
};
```typescript

### rate limit behavior

1 **per user**: rate limits are tracked per user (stored in localstorage)
2 **rolling window**: counts are based on rolling time windows
3 **automatic cleanup**: old entries are cleaned up daily
4 **persistence**: state persists across page refreshes

---

## validation config

validation rules in `lib/email/config.ts`:

```typescript
export const VALIDATION = {
  // RFC 5322 compliant email regex
  emailRegex: /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,

  // Maximum subject length
  maxSubjectLength: 200,

  // Maximum HTML body size (bytes)
  maxHtmlBodySize: 1024 * 1024,  // 1MB

  // Maximum number of attachments
  maxAttachments: 10,

  // Maximum attachment size (bytes)
  maxAttachmentSize: 10 * 1024 * 1024,  // 10MB

  // Maximum recipients per email
  maxRecipients: 50,
};
```typescript

### validation behavior

| validation | behavior |
|------------|----------|
| email format | rejects invalid email formats |
| subject length | truncates or rejects long subjects |
| html body size | rejects oversized html |
| attachment count | rejects if too many attachments |
| attachment size | rejects oversized attachments |
| recipient count | rejects if too many recipients |

---

## hook config

default hook configuration in `lib/email/config.ts`:

```typescript
export const DEFAULT_HOOK_CONFIG: EmailHookConfig = {
  // Rate limiting
  enableRateLimiting: true,
  maxEmailsPerMinute: 10,
  maxEmailsPerHour: 100,
  maxEmailsPerDay: 500,

  // Duplicate detection
  enableDuplicateDetection: true,
  duplicateWindowSeconds: 60,

  // Bulk sending
  requireBulkConfirmation: true,
  bulkThreshold: 10,

  // Domain restrictions
  allowedRecipientDomains: [],  // Empty = all allowed
  blockedRecipientDomains: [
    "mailinator.com",
    "guerrillamail.com",
    "tempmail.com",
    "throwaway.email",
    "fakeinbox.com",
    "10minutemail.com",
    "temp-mail.org",
    "getnada.com",
  ],

  // Validation
  enableEmailValidation: true,

  // Storage
  storeInDatabase: true,
};
```typescript

### hook config options

| option | type | default | description |
|--------|------|---------|-------------|
| `enableratelimiting` | boolean | true | enable rate limiting |
| `maxemailsperminute` | number | 10 | max emails per minute |
| `maxemailsperhour` | number | 100 | max emails per hour |
| `maxemailsperday` | number | 500 | max emails per day |
| `enableduplicatedetection` | boolean | true | detect duplicate emails |
| `duplicatewindowseconds` | number | 60 | duplicate detection window |
| `requirebulkconfirmation` | boolean | true | require confirmation for bulk |
| `bulkthreshold` | number | 10 | threshold for bulk confirmation |
| `allowedrecipientdomains` | string[] | [] | allowed domains (empty = all) |
| `blockedrecipientdomains` | string[] | [] | blocked domains |
| `enableemailvalidation` | boolean | true | validate email formats |
| `storeindatabase` | boolean | true | store emails in database |
| `onsuccess` | function | - | success callback |
| `onerror` | function | - | error callback |
| `onratelimited` | function | - | rate limited callback |

### configuration presets

#### high volume

```typescript
const { sendEmail } = useSendEmail({
  maxEmailsPerMinute: 50,
  maxEmailsPerHour: 500,
  maxEmailsPerDay: 5000,
  enableDuplicateDetection: false,
  requireBulkConfirmation: false,
});
```typescript

#### strict security

```typescript
const { sendEmail } = useSendEmail({
  maxEmailsPerMinute: 3,
  maxEmailsPerHour: 20,
  maxEmailsPerDay: 100,
  enableDuplicateDetection: true,
  duplicateWindowSeconds: 300,  // 5 minutes
  allowedRecipientDomains: ["company.com", "partner.com"],
  requireBulkConfirmation: true,
  bulkThreshold: 5,
});
```typescript

#### internal only

```typescript
const { sendEmail } = useSendEmail({
  allowedRecipientDomains: [
    "suitsbooks.com",
    "suitsfinance.com",
  ],
  blockedRecipientDomains: [],  // Clear default blocked
});
```typescript

#### testing/development

```typescript
const { sendEmail } = useSendEmail({
  enableRateLimiting: false,
  enableDuplicateDetection: false,
  storeInDatabase: false,
  onSuccess: (response) => console.log("Sent:", response),
  onError: (error) => console.error("Error:", error),
});
```typescript

---

## error messages

customizable error messages in `lib/email/config.ts`:

```typescript
export const ERROR_MESSAGES = {
  RATE_LIMIT_MINUTE: "Too many emails sent. Please wait a minute before sending more.",
  RATE_LIMIT_HOUR: "Hourly email limit reached. Please try again later.",
  RATE_LIMIT_DAY: "Daily email limit reached. Please try again tomorrow.",
  DUPLICATE_EMAIL: "This email was recently sent. Please wait before sending again.",
  INVALID_EMAIL: "Invalid email address provided.",
  BLOCKED_DOMAIN: "Emails to this domain are not allowed.",
  MISSING_TEMPLATE: "Email template not found.",
  MISSING_SUBJECT: "Email subject is required.",
  MISSING_RECIPIENT: "Email recipient is required.",
  MISSING_API_KEY: "Email service not configured.",
  SEND_FAILED: "Failed to send email. Please try again.",
  BULK_CONFIRMATION_REQUIRED: "Bulk email send requires confirmation.",
  ATTACHMENT_TOO_LARGE: "Attachment exceeds maximum size limit.",
  TOO_MANY_ATTACHMENTS: "Too many attachments. Maximum is 10.",
  TOO_MANY_RECIPIENTS: "Too many recipients. Maximum is 50.",
  UNAUTHORIZED: "You are not authorized to send emails.",
};
```typescript

### customizing error messages

modify `error_messages` in `lib/email/config.ts`:

```typescript
export const ERROR_MESSAGES = {
  // ... existing messages
  
  RATE_LIMIT_MINUTE: "Slow down! You can only send 10 emails per minute.",
  // Custom message
};
```typescript

---

## domain configuration

### adding blocked domains

```typescript
// In lib/email/config.ts
export const DEFAULT_HOOK_CONFIG: EmailHookConfig = {
  blockedRecipientDomains: [
    // Existing
    "mailinator.com",
    "guerrillamail.com",
    
    // Add more
    "yopmail.com",
    "trashmail.com",
    "fakeemail.com",
  ],
};
```typescript

### using domain allowlist

```typescript
const { sendEmail } = useSendEmail({
  // Only allow these domains
  allowedRecipientDomains: [
    "suitsbooks.com",
    "gmail.com",
    "outlook.com",
  ],
  
  // Clear blocked list when using allowlist
  blockedRecipientDomains: [],
});
```typescript

---

## resend configuration

### verify your domain

1 go to [resend domains](https://resend.com/domains)
2 add your domain (eg `suitsbooks.com`)
3 add dns records (spf dkim dmarc)
4 wait for verification

### api key scopes

when creating your api key consider:

- **full access**: can send and manage emails
- **sending only**: can only send emails (recommended for production)

### webhook configuration

resend can send webhooks for email events configure at [resend webhooks](https://resend.com/webhooks)

events available:

- `email.sent`
- `email.delivered`
- `email.delivery_delayed`
- `email.complained`
- `email.bounced`
- `email.opened`
- `email.clicked`

---

## best practices

### 1 use environment variables

never hardcode api keys or sensitive configuration:

```typescript
//  Good
const apiKey = process.env.RESEND_API_KEY;

//  Bad
const apiKey = "re_123456789";
```typescript

### 2 configure per environment

use different settings for different environments:

Development

EMAIL_TEST_MODE=true RESEND_FROM_EMAIL=dev@suitsbooks.com

Production

EMAIL_TEST_MODE=false RESEND_FROM_EMAIL=noreply@suitsbooks.com


### 3 set appropriate rate limits

consider your use case:

- **transactional**: lower limits stricter duplicate detection
- **marketing**: higher limits bulk confirmation
- **internal**: domain restrictions

### 4 monitor usage

track email metrics:

- sent count
- delivery rate
- open rate
- bounce rate

### 5 handle errors gracefully

always check for errors and provide user feedback:

```typescript
const result = await sendEmail({ ... });
if (!result.success) {
  notification({
    message: result.error,
    success: false,
  });
}
```typescript