Core concepts
Failover policies
A failover policy says "if my primary destination misbehaves, swap to a backup." It runs after the routing decision picks a primary the policy attaches a fallback chain to that primary.
Policy shape
Each policy is keyed by (user, primary_destination_id). The fields:
- Primary destination id: the destination this policy attaches to. Set by the routing engine when a rule (or the default) picks this destination.
- Fallback destination ids: an ordered list of backup destinations to try, in order, when the primary fails.
- Triggers: which kinds of failure switch to the next destination. Independent toggles:
on_5xx: the provider responded with a 5xx status (server error). Default on.on_rate_limit: the provider responded with 429 or its proprietary rate-limit signal. Default on.on_timeout: the provider didn't respond withintimeout_seconds. Default on.timeout_seconds: how long to wait before declaring a timeout. Default 30s.max_attempts: cap on total attempts across primary + fallbacks. Default 3.- Enabled (boolean): toggle without deleting.
How it runs
- Routing picks the primary destination.
- We call the primary. If it succeeds (2xx), the trace gets
provider_responsewith the success payload done. - If the primary fails in a way that matches one of the configured triggers, the trace gets a
failover_triggeredevent and we move to the first fallback in the chain. - The next fallback is called the same way. If it succeeds, the trace ends with
provider_responseon the fallback. - If we exhaust the chain (or hit
max_attempts), the trace's final status isfailed.
Each attempt is a separate provider_attempt + provider_response pair on the trace. You can see the full chain on the trace detail page.
Common patterns
Cloud-primary, SMTP-fallback
You normally send through SES, but your AWS account has rate limits you don't want customers to feel:
primary: ses-prod
fallback: [smtp-hostpoint]
triggers: on_5xx, on_rate_limit, on_timeoutTwo clouds
You want resilience against a single provider's outage:
primary: postmark-prod
fallback: [resend-prod, smtp-emergency]
triggers: on_5xx, on_timeout
max_attempts: 3The third hop (smtp-emergency) is a last-resort host that's always-on but has lower deliverability better than dropping the message entirely.
Region-locked failover
Inside a single provider, fail over between regions:
primary: ses-eu-central-1
fallback: [ses-us-east-1]
triggers: on_5xx, on_timeout
on_rate_limit: false (the same throttle bucket pointless)What failover doesn't do
- It doesn't retry the same destination. If the primary times out, we go to the fallback, not back to the primary. Use the provider's own retry budget for that.
- It doesn't restart after a delivery webhook bounces. Once the provider says "accepted," the trace is no longer in the failover state machine. Bounce webhooks arrive later and don't trigger a re-send.
- It doesn't re-route on 4xx. A 4xx response means the provider rejected the request as invalid (bad from-address, malformed body) sending the same payload to the fallback would just fail again. The trace ends with the primary's failure.
To configure failover, hit `/destinations/failover` or use the MCP server: email_failover_policy_set.