Cloudflare announced on April 16, 2026 that Email Service is in public beta with a clear focus on agent workflows.
If you build inbox automations, support copilots, or async back-office agents, this release matters because the send and reply loop can stay inside Cloudflare Workers.
What changed in the beta
Based on Cloudflare’s announcement, the practical upgrades are:
- Native email sending from Workers so your app can send without bolting on another provider API for the basic flow.
- Email MCP + Wrangler tooling so coding agents and developer workflows can configure and trigger email tasks faster.
- Async-friendly agent patterns so a Worker can process in the background and reply when work is done.
That combination is especially useful for:
- support follow-ups after async investigation,
- invoice or export-ready notifications,
- approval and human-in-the-loop email steps,
- long-running agent jobs that need a final threaded reply.
Temp API lib that checks UnwrapEmail before send
Instead of maintaining a temporary regex and disposable-domain list in your Worker, you can call UnwrapEmail first and only send when the address is safe.
type UnwrapEmailValidationResponse = {
safe_to_send?: boolean
is_disposable?: boolean
has_valid_mx_records?: boolean
reason?: string
}
export type TempEmailPreflightInput = {
email: string
apiKey: string
timeoutMs?: number
}
export type TempEmailPreflightResult =
| { ok: true }
| { ok: false; reason: string }
export const runTempEmailPreflight = async (
input: TempEmailPreflightInput,
): Promise<TempEmailPreflightResult> => {
const controller = new AbortController()
const timeout = setTimeout(() => {
controller.abort()
}, input.timeoutMs ?? 4_000)
try {
const response = await fetch(
`https://api.unwrap.email/v1/emails/validate?email=${encodeURIComponent(input.email)}&validate_domain=1`,
{
method: 'GET',
headers: {
Authorization: `Bearer ${input.apiKey}`,
},
signal: controller.signal,
},
)
if (!response.ok) {
return {
ok: false,
reason: `UnwrapEmail request failed with status ${response.status}`,
}
}
const validation = (await response.json()) as UnwrapEmailValidationResponse
if (!validation.safe_to_send) {
return {
ok: false,
reason: validation.reason ?? 'Address is not safe to send.',
}
}
if (validation.is_disposable) {
return {
ok: false,
reason: 'Disposable inboxes are blocked for transactional sends.',
}
}
if (!validation.has_valid_mx_records) {
return {
ok: false,
reason: 'Domain MX records are invalid for delivery.',
}
}
return { ok: true }
} catch (error) {
const reason =
error instanceof Error ? error.message : 'Unknown validation failure.'
return {
ok: false,
reason: `UnwrapEmail request failed: ${reason}`,
}
} finally {
clearTimeout(timeout)
}
}
Send wrapper for Cloudflare Worker email binding
This wrapper enforces the UnwrapEmail preflight check before any Worker send operation.
import { runTempEmailPreflight } from './temp-email-preflight'
type WorkerEmailBinding = Pick<SendEmail, 'send'>
type SendTransactionalEmailInput = {
to: string
from: string
subject: string
text: string
unwrapEmailApiKey: string
}
export const sendTransactionalEmail = async (
email: WorkerEmailBinding,
input: SendTransactionalEmailInput,
): Promise<void> => {
const preflight = await runTempEmailPreflight({
email: input.to,
apiKey: input.unwrapEmailApiKey,
})
if (!preflight.ok) {
throw new Error(
`Email blocked by UnwrapEmail preflight: ${preflight.reason}`,
)
}
await email.send({
to: input.to,
from: input.from,
subject: input.subject,
text: input.text,
})
}
Next hardening steps
- Add retry + circuit-breaker handling around UnwrapEmail fetch failures.
- Cache preflight results briefly to reduce duplicate checks for repeated sends.
- Add per-tenant send rate limits and abuse alerting.
- Log blocked attempts with clear reason codes for auditability.
- Add unit tests for allowed and blocked preflight outcomes.