> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usesendly.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Best Practices

> Tips to maximize your email deliverability

## 1. Domain Authentication

One of the most effective ways to ensure deliverability is to authenticate your domain.

### Configure DKIM, SPF and DMARC

```javascript theme={null}
// 1. Create the domain
const domain = await client.domains.create({
  domain: 'mail.yourdomain.com'
})

// 2. Follow the instructions to add DNS records
// 3. Verify the domain
await client.domains.verify(domain.id)

// 4. Now you can send from that domain
await client.emails.send({
  from: 'noreply@mail.yourdomain.com',
  to: 'user@example.com',
  subject: 'Authenticated email',
  html: '<p>This email is authenticated</p>'
})
```

### Verify DNS Records

Typical DNS records you will need:

| Type  | Name                            | Value              |
| ----- | ------------------------------- | ------------------ |
| CNAME | mail.\_domainkey                | provided-by-sendly |
| TXT   | v=spf1 include:sendly.com \~all | SPF                |
| TXT   | v=DMARC1                        | DMARC              |

## 2. List Management

### Clean your list regularly

```javascript theme={null}
// Keep a record of bounces
const logs = await client.logs.list({
  status: 550 // Permanent bounces
})

// Automatically unsubscribe after N bounces
const bouncedEmails = logs.data
  .filter(log => log.status === 550)
  .map(log => log.request_body?.to)
```

### Use double opt-in (DOI)

```javascript theme={null}
// 1. User signs up
// 2. Send confirmation email
await client.emails.send({
  from: 'noreply@yourdomain.com',
  to: email,
  subject: 'Confirm your email',
  html: `<a href="https://yourapp.com/confirm?token=xyz">Confirm</a>`
})

// 3. Only add to the list after confirmation
```

## 3. Quality Content

### Avoid spam words

These words can reduce deliverability:

* "Free", "Limited time", "Act now"
* "Click here", "Buy now"
* "Spam", "Viagra", "Casino"

### Use clean HTML

```javascript theme={null}
await client.emails.send({
  from: 'noreply@yourdomain.com',
  to: 'user@example.com',
  subject: 'Clear subject',
  html: `
    <!DOCTYPE html>
    <html>
      <head>
        <style>
          body { font-family: Arial, sans-serif; }
        </style>
      </head>
      <body>
        <h1>Welcome</h1>
        <p>Content here</p>
      </body>
    </html>
  `
})
```

### Include an unsubscribe link

```javascript theme={null}
await client.emails.send({
  from: 'noreply@yourdomain.com',
  to: 'user@example.com',
  subject: 'Newsletter',
  html: `
    <h1>My Newsletter</h1>
    <p>Content...</p>
    <hr />
    <a href="https://yourapp.com/unsubscribe?email=user@example.com">
      Unsubscribe
    </a>
  `
})
```

## 4. Monitoring and Analytics

### Track deliveries

```javascript theme={null}
const response = await client.emails.send({
  from: 'noreply@yourdomain.com',
  to: 'user@example.com',
  subject: 'Email with tracking',
  html: '<p>Content</p>',
  tracking: {
    open: true,
    click: true
  }
})

const email = await client.emails.get(response.id)
console.log(`Status: ${email.status}`)
console.log(`Sent at: ${email.sent_at}`)
```

### Analyze logs

```javascript theme={null}
const logs = await client.logs.list({
  sortOrder: 'desc',
  limit: 100
})

const stats = {
  total: logs.data.length,
  delivered: logs.data.filter(l => l.status === 200).length,
  bounced: logs.data.filter(l => l.status === 550).length,
  rejected: logs.data.filter(l => l.status === 421).length
}

console.log('Delivery rate:',
  (stats.delivered / stats.total * 100).toFixed(2) + '%'
)
```

## 5. Webhooks for Notifications

### Set up webhooks for events

```javascript theme={null}
const webhook = await client.webhooks.create({
  url: 'https://yourapp.com/webhooks/sendly',
  events: [
    'email.delivered',
    'email.bounced',
    'email.opened',
    'email.clicked'
  ]
})

console.log('Webhook ID:', webhook.webhook.id)
console.log('Signing secret:', webhook.signing_key)
```

### Validate webhooks on your server

```javascript theme={null}
import crypto from 'crypto'

function validateWebhook(payload, signature, secret) {
  const hash = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex')

  return hash === signature
}

app.post('/webhooks/sendly', (req, res) => {
  const signature = req.headers['x-sendly-signature']
  const payload = req.body

  if (!validateWebhook(JSON.stringify(payload), signature, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature')
  }

  handleWebhookEvent(payload)
  res.send('OK')
})
```

## 6. Performance and Scalability

### Use processing queues

```javascript theme={null}
import Queue from 'bull'

const emailQueue = new Queue('emails')

emailQueue.add({
  from: 'noreply@yourdomain.com',
  to: 'user@example.com',
  subject: 'Email',
  html: '<p>Content</p>'
}, {
  attempts: 3,
  backoff: {
    type: 'exponential',
    delay: 2000
  }
})

emailQueue.process(async (job) => {
  const client = new Sendly()
  return await client.emails.send(job.data)
})
```

### Bulk send with rate control

```javascript theme={null}
async function sendBulkEmails(recipients, emailTemplate) {
  const BATCH_SIZE = 50
  const DELAY_BETWEEN_BATCHES = 1000 // 1 second

  for (let i = 0; i < recipients.length; i += BATCH_SIZE) {
    const batch = recipients.slice(i, i + BATCH_SIZE)

    const promises = batch.map(recipient =>
      client.emails.send({
        from: emailTemplate.from,
        to: recipient.email,
        subject: emailTemplate.subject,
        html: emailTemplate.html,
        idempotencyKey: `${recipient.id}-${Date.now()}`
      })
    )

    await Promise.all(promises)

    if (i + BATCH_SIZE < recipients.length) {
      await new Promise(r => setTimeout(r, DELAY_BETWEEN_BATCHES))
    }
  }
}
```

## 7. Security

### Never hardcode API keys

```javascript theme={null}
// BAD
const client = new Sendly('se_abc123xyz')

// GOOD
const client = new Sendly(process.env.SENDLY_API_KEY)
```

### Validate email addresses

```javascript theme={null}
function isValidEmail(email) {
  const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  return re.test(email) && email.length <= 254
}

try {
  if (!isValidEmail(to)) {
    throw new Error('Invalid email')
  }
  await client.emails.send({ from, to, ... })
} catch (error) {
  console.error('Invalid email:', error.message)
}
```

### Use HTTPS for webhooks

```javascript theme={null}
// GOOD - HTTPS
await client.webhooks.create({
  url: 'https://yourapp.com/webhooks/sendly',
  events: ['email.delivered']
})

// BAD - HTTP
// await client.webhooks.create({
//   url: 'http://yourapp.com/webhooks/sendly',
//   events: ['email.delivered']
// })
```

## 8. Testing

### Test before production

```javascript theme={null}
async function testEmail() {
  try {
    await client.emails.send({
      from: 'test@mail.yourdomain.com',
      to: 'test@yourapp.com',
      subject: 'Test email',
      html: '<p>Testing</p>'
    })
  } catch (error) {
    console.error('Test error:', error)
  }
}

await testEmail()
```

### Monitor in staging

```javascript theme={null}
const logs = await client.logs.list({
  sortOrder: 'desc',
  limit: 50
})

logs.data.forEach(log => {
  console.log(`[${log.created_at}] ${log.url} - ${log.status}`)
})
```

## Final Checklist

* Domain verified with DKIM/SPF/DMARC
* Environment variable for API key
* Error handling implemented
* Automatic retries configured
* Webhooks for important events
* Active log monitoring
* Clean email list
* Staging tests completed
* Email content optimized
* Unsubscribe link included

## Support

Need help optimizing your setup? Contact [support@usesendly.app](mailto:support@usesendly.app)
