Skip to main content

Security Best Practices

Protect your account, API keys, and data.

API Key Security

Environment Variables

Never hardcode API keys:
// ❌ Bad
const lettr = new Lettr('le_xxxxxxxxxxxx');

// ✅ Good
const lettr = new Lettr(process.env.LETTR_API_KEY);

.gitignore

Ensure secrets aren’t committed:
# .gitignore
.env
.env.local
.env.*.local

Key Rotation

Rotate keys regularly:
// 1. Create new key
const newKey = await lettr.apiKeys.create({
  name: 'Production - Q2 2024',
  permissions: ['emails:send']
});

// 2. Update your application with new key

// 3. Delete old key
await lettr.apiKeys.delete('old_key_id');

Minimum Permissions

Only grant necessary permissions:
// ❌ Bad - Full access when only sending needed
const key = await lettr.apiKeys.create({
  permissions: ['*']
});

// ✅ Good - Only required permissions
const key = await lettr.apiKeys.create({
  permissions: ['emails:send']
});

Webhook Security

Always Verify Signatures

import { verifyWebhook } from 'lettr';

app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  try {
    const event = verifyWebhook(
      req.body,
      req.headers['lettr-signature'],
      process.env.LETTR_WEBHOOK_SECRET
    );
    // Process verified event
  } catch (err) {
    // Reject unverified requests
    return res.status(400).send('Invalid signature');
  }
});

IP Allowlisting

Restrict webhook origins:
const allowedIPs = ['203.0.113.10', '203.0.113.11'];

app.post('/webhooks', (req, res, next) => {
  const clientIP = req.ip;
  if (!allowedIPs.includes(clientIP)) {
    return res.status(403).send('Forbidden');
  }
  next();
});

Account Security

Enable Two-Factor Authentication

Require 2FA for all team members:
await lettr.team.updateSettings({
  require2FA: true
});

Review Team Access

Regularly audit team members:
const members = await lettr.team.list();

members.forEach(member => {
  console.log({
    email: member.email,
    role: member.role,
    lastLogin: member.lastLoginAt
  });
});

Use SSO (Enterprise)

Enterprise customers should use SSO for centralized access management.

Data Protection

Sensitive Data in Emails

Don’t include sensitive data in email content that’s logged:
// ❌ Bad - Password in email
await lettr.emails.send({
  html: `Your password is: ${password}`
});

// ✅ Good - Secure reset link
await lettr.emails.send({
  html: `Click to reset: ${resetUrl}`
});

Metadata Security

Don’t store sensitive data in metadata:
// ❌ Bad
await lettr.emails.send({
  metadata: { ssn: '123-45-6789' }
});

// ✅ Good - Use opaque references
await lettr.emails.send({
  metadata: { userId: 'user_123' }
});

Monitoring

Set Up Alerts

Monitor for suspicious activity:
// Alert on unusual sending patterns
const stats = await lettr.stats.get({ period: 'today' });

if (stats.sent > normalDailyAverage * 2) {
  alertSecurityTeam('Unusual sending volume detected');
}

Audit Logs

Review account activity:
const logs = await lettr.auditLogs.list({
  startDate: '2024-01-01'
});

// Look for suspicious activity
logs.forEach(log => {
  if (log.action === 'api_key.created' || log.action === 'team.invited') {
    console.log(log);
  }
});

Security Checklist

1

API Keys in Environment Variables

Never hardcode keys in source code.
2

Minimum Permissions

Only grant permissions actually needed.
3

Webhook Signature Verification

Always verify webhook signatures.
4

Two-Factor Authentication

Enable 2FA for all team members.
5

Regular Key Rotation

Rotate API keys every 90 days.
6

Access Reviews

Audit team access quarterly.

Reporting Security Issues

Found a vulnerability? Report responsibly: We respond within 24 hours and offer a bug bounty program.