# Email Workers Patterns

## Parse Email

```typescript
import PostalMime from "postal-mime";

export default {
  async email(message, env, ctx) {
    const buffer = await new Response(message.raw).arrayBuffer();
    const email = await PostalMime.parse(buffer);
    console.log(email.from, email.subject, email.text, email.attachments.length);
    await message.forward("inbox@example.com");
  },
};
```

## Filtering

```typescript
// Allowlist from KV
const allowList = (await env.ALLOWED_SENDERS.get("list", "json")) || [];
if (!allowList.includes(message.from)) {
  message.setReject("Not allowed");
  return;
}

// Size check (avoid parsing large emails)
if (message.rawSize > 5_000_000) {
  await message.forward("inbox@example.com"); // Forward without parsing
  return;
}
```

## Auto-Reply with Threading

```typescript
import { EmailMessage } from "cloudflare:email";
import { createMimeMessage } from "mimetext";

const msg = createMimeMessage();
msg.setSender({ addr: "support@example.com" });
msg.setRecipient(message.from);
msg.setSubject(`Re: ${message.headers.get("Subject")}`);
msg.setHeader("In-Reply-To", message.headers.get("Message-ID") || "");
msg.addMessage({ contentType: "text/plain", data: "Thank you. We will respond." });

await message.reply(new EmailMessage("support@example.com", message.from, msg.asRaw()));
```

## Rate-Limited Auto-Reply

```typescript
const rateKey = `rate:${message.from}`;
if (!(await env.RATE_LIMIT.get(rateKey))) {
  // Send reply...
  ctx.waitUntil(env.RATE_LIMIT.put(rateKey, "1", { expirationTtl: 3600 }));
}
```

## Subject-Based Routing

```typescript
const subject = (message.headers.get("Subject") || "").toLowerCase();
if (subject.includes("billing")) await message.forward("billing@example.com");
else if (subject.includes("support")) await message.forward("support@example.com");
else await message.forward("general@example.com");
```

## Multi-Tenant Routing

```typescript
// support+tenant123@example.com → tenant123
const tenantId = message.to.split("@")[0].match(/\+(.+)$/)?.[1] || "default";
const config = await env.TENANT_CONFIG.get(tenantId, "json");
config?.forwardTo ? await message.forward(config.forwardTo) : message.setReject("Unknown");
```

## Archive & Extract Attachments

```typescript
// Archive to KV
ctx.waitUntil(
  env.ARCHIVE.put(
    `email:${Date.now()}`,
    JSON.stringify({
      from: message.from,
      subject: email.subject,
    })
  )
);

// Attachments to R2
for (const att of email.attachments) {
  ctx.waitUntil(env.R2.put(`${Date.now()}-${att.filename}`, att.content));
}
```

## Webhook Integration

```typescript
ctx.waitUntil(
  fetch(env.WEBHOOK_URL, {
    method: "POST",
    body: JSON.stringify({ from: message.from, subject: message.headers.get("Subject") }),
  }).catch(err => console.error(err))
);
```
