Files
plunk/apps/wiki/content/docs/guides/scaling-email.mdx
T

278 lines
6.2 KiB
Plaintext

---
title: Scaling Email Delivery
description: Best practices for high-volume email sending
---
## Email delivery at scale
Plunk is built on AWS SES and handles millions of emails. Follow these practices to maintain high deliverability and performance at scale.
## Deliverability best practices
### Use custom domains
Emails from custom domains have higher trust and better deliverability than shared domains.
**Setup:**
1. Go to **Settings > Domains**
2. Add your domain
3. Configure DNS records (DKIM, SPF)
4. Wait for verification
[Learn more about custom domains →](/guides/custom-domains)
### Warm up new domains
Start small and gradually increase volume.
This builds sender reputation with email providers.
### Clean your list regularly
Remove bounced and inactive contacts:
```javascript
// Get bounced contacts
const bounced = await fetch('{{API_URL}}/events?name=email.bounced&limit=1000', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
// Unsubscribe them
for (const event of bounced.data.events) {
await fetch(`{{API_URL}}/contacts/${event.contactId}`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ subscribed: false })
});
}
```
**When to clean:**
- Hard bounces: Immediately unsubscribe
- Soft bounces: After 3 attempts
- No engagement: After 6-12 months
### Segment your audience
Send relevant content to engaged users:
```javascript
// Create engaged users segment
{
"name": "Engaged Users",
"filters": {
"operator": "OR",
"conditions": [
{ "field": "data.lastOpenedAt", "operator": "greaterThan", "value": "{{90 days ago}}" },
{ "field": "data.lastClickedAt", "operator": "greaterThan", "value": "{{90 days ago}}" }
]
}
}
```
Send campaigns to engaged segments for better rates.
## Tracking control
You can disable tracking in your project settings for privacy-focused audiences or to reduce email size.
**When tracking is disabled:**
- No tracking pixel (no open tracking)
- Links not rewritten (no click tracking)
- Smaller email size
- May improve deliverability for privacy-conscious audiences
**When to disable:**
- Regulated industries (healthcare, finance)
- Privacy-focused users
- Transactional emails where tracking isn't needed
- High-volume sends where analytics aren't critical
## Rate limits
### AWS SES limits
Plunk automatically queues emails to stay within limits. Large sends process in background.
### Increase limits
For higher throughput:
1. Maintain good sender reputation
2. Consistent sending volume
3. Low bounce/complaint rates
4. Request limit increase from AWS
### Batch operations
For bulk operations, use appropriate endpoints:
```javascript
// ✓ Good: Single request for multiple recipients
fetch('{{API_URL}}/v1/send', {
method: 'POST',
body: JSON.stringify({
to: ['user1@example.com', 'user2@example.com', 'user3@example.com'],
subject: 'Update',
body: 'Message'
})
});
// ✗ Avoid: Multiple requests
for (const email of emails) {
await fetch('{{API_URL}}/v1/send', {...}); // Sequential, slow
}
```
## Campaign targeting
### Dynamic filtering
Target audiences without creating segments:
```javascript
{
"name": "Premium Launch",
"audienceType": "FILTERED",
"audienceFilter": {
"operator": "AND",
"conditions": [
{ "field": "data.plan", "operator": "equals", "value": "premium" },
{ "field": "data.signupDate", "operator": "greaterThan", "value": "2025-01-01" },
{ "field": "subscribed", "operator": "equals", "value": true }
]
},
"templateId": "template_id"
}
```
**Use FILTERED for:**
- One-time sends
- Testing targeting
- Very specific criteria
**Use SEGMENT for:**
- Repeated targeting
- Workflow triggers
- Segment analytics
## Segment membership tracking
For segments used in workflows, enable membership tracking:
```bash
curl -X POST {{API_URL}}/segments \
-H "Authorization: Bearer sk_your_secret_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Active Premium Users",
"filters": {...},
"trackMembership": true
}'
```
**Enable for:**
- Workflow trigger segments
- Lifecycle stage tracking
- Cohort analysis
**Disable for:**
- Large segments (100k+ contacts)
- Campaign-only segments
- Frequently changing segments
Membership updates run every 5 minutes in background.
## Performance optimization
### Cache contact data
For high-volume API sends, cache contact lookups:
```javascript
// Cache contact IDs locally
const contactCache = new Map();
async function getContactId(email) {
if (contactCache.has(email)) {
return contactCache.get(email);
}
const contact = await fetch(`{{API_URL}}/contacts?search=${email}`);
contactCache.set(email, contact.id);
return contact.id;
}
```
### Use webhooks for async processing
Instead of waiting for email sends:
```javascript
// Workflow with webhook for confirmation
{
"steps": [
{ "type": "SEND_EMAIL", "config": {...} },
{
"type": "WEBHOOK",
"config": {
"url": "https://your-api.com/email-sent",
"method": "POST",
"body": {
"contactId": "{{id}}",
"emailId": "{{emailId}}"
}
}
}
]
}
```
### Batch workflow triggers
Trigger workflows in batches instead of one at a time:
```javascript
// Batch event tracking
const events = users.map(user => ({
email: user.email,
event: 'welcome_campaign',
data: { userId: user.id }
}));
// Send in parallel (respecting rate limits)
await Promise.all(
events.map(event =>
fetch('{{API_URL}}/v1/track', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(event)
})
)
);
```
## Monitoring
### Track key metrics
Monitor these regularly:
- **Bounce rate** — Should be < 2%
- **Complaint rate** — Should be < 0.1%
- **Open rate** — Industry average 15-25%
- **Click rate** — Industry average 2-5%
Monitor these metrics in your dashboard analytics page to track deliverability and engagement over time.
## Next Steps
- [Set billing limits](/guides/billing-limits) to control costs
- [Monitor analytics](/guides/analytics) for deliverability
- [Troubleshoot issues](/guides/troubleshooting) if problems arise