There is no universal channel order
Most fallback designs start with a ladder — try this channel, then that one — and then discover the ladder is wrong for half the journeys it is applied to. The order is an output, not an input.
The inputs are the business objective and the useful deadline. Everything else follows from them.
What the policy actually evaluates
Before each attempt, for this recipient and this message:
- Objective. What has to happen: a code entered, an action taken, a fact recorded, a reply received. This determines what counts as success and therefore when to stop.
- Useful deadline. The point after which delivery no longer helps. A code the user is waiting for and a delivery notice for tomorrow have different deadlines, and a chain that outlives the deadline is wasted spend.
- Consent and preference. Whether the recipient has a consent basis for this channel and this content, and whether they have expressed a preference. A stated preference outranks cost.
- Verified destination. Whether the address or number is verified and current for that channel, rather than assumed from another record.
- Channel availability and market support. Whether the channel is available to that recipient in that market, and whether your sender is registered or approved to use it there. Availability differs by market, device and operator, and it changes.
- Urgency and sensitivity. Some content should not go to a channel whose notification content is visible on a lock screen, or whose transcript may be shared.
- Cost. Relevant, but bounded by the objective. Cost per completed outcome is the figure that matters, not cost per message.
- Signal reliability. Whether the channel returns a signal you can act on, and how ambiguous that signal is.
Signals you can and cannot trust
A fallback decision is only as good as the signal it reacts to.
| Signal | What it supports | What it does not support |
|---|---|---|
| Rejection at submission | Fail fast, try the next eligible channel | Nothing about the recipient |
| Final failure status from the channel | Reasonable confidence the message did not arrive | Whether a later retry would succeed |
| Delivery confirmation | The channel reported it reached the device | That anyone saw it |
| Read indication, where offered | The message was opened on that device | Attention, comprehension or intent, and it may be disabled by the recipient |
| No status within the deadline | Ambiguity, not failure | A safe assumption either way |
| Recipient action (verified, clicked, replied, paid) | Success; stop the chain | — |
Two consequences. Absence of a signal is not failure, so a chain that treats silence as failure will duplicate messages. And a read indication is not attention, so a chain that requires one before it stops will over-send to people who have already acted.
One journey, one identity
The chain needs a single journey or correlation identifier created when the application decides to communicate, carried across every channel attempt, every callback and the outcome record. Without it, cross-channel deduplication, attribution and cost per outcome are all guesses.
Rules that depend on it:
- Eligibility is re-evaluated before every attempt, not once at the start. Consent, verification and channel availability can change between attempts, as can the underlying business state.
- Idempotency and deduplication apply at journey level, not per channel. An application-generated key per attempt, stored before submission, plus a rule that a new attempt cannot begin while another is in a non-final state, unless its deadline has passed.
- Attempt state and final state are separate. Each attempt has its own lifecycle; the journey has one outcome.
- Stop conditions come first. If the objective is met, or the source business state changed (order cancelled, invoice paid, session ended, code already verified), the chain stops even if attempts are pending.
- Late and out-of-order callbacks are expected. A delivery confirmation can arrive after the fallback has been sent. Record it, do not let it re-open a completed journey, and count it in attribution.
Serial or parallel
Serial is the default: it respects the recipient and keeps attribution clean. Parallel is defensible when the consequence of a missed message clearly exceeds the cost of duplication — safety, fraud or outage notices — or when the deadline is too short for a serial chain to complete. Decide it per journey, write it down, and make it visible in reporting, because parallel sends inflate reach numbers and distort channel comparison.
For a code the user is waiting for, the constraint is the deadline rather than a rule about which channels are allowed: any channel whose signal and latency you can rely on inside that window is a candidate, and duplicate issuance for the same session is the failure to avoid.
Decision table
| Journey | Objective and deadline | Ordering logic | Stop conditions |
|---|---|---|---|
| Authentication code | Code entered inside the live session | Fastest channel with a reliable signal for that market and verified destination; parallel only if the deadline cannot be met serially | Verification succeeds, session ends, deadline passes. Never issue a second code for the same session |
| Delivery or appointment notice | Recipient informed before the event | Preferred channel first, then next eligible with a reliable signal, spaced against the event time | Recipient acknowledges, event occurs or is cancelled |
| Payment request | Payment made before due date | Channel with consent and identity confidence; escalate to a channel that supports reply and human contact | Payment received, plan agreed, account state changes |
| Support follow-up | Reply received | The channel the conversation started on, then a channel the recipient has used | Reply received, ticket closed |
| Promotional | Consented engagement | Cheapest consented channel; no cross-channel repetition of the same offer | Opt-out, frequency cap, campaign window ends |
| Urgent operational | Acknowledgement | Parallel across eligible channels, then human escalation | Acknowledgement, or escalation path completes |
State machine
journey(id) states: PLANNED -> ATTEMPTING -> COMPLETED | STOPPED | EXHAUSTED
on plan:
resolve objective, deadline, stop conditions
build ordered candidate list (eligibility evaluated per candidate)
on attempt(candidate):
if now > deadline -> EXHAUSTED
if stop condition met -> STOPPED
re-evaluate eligibility(candidate) -> if ineligible, next candidate
create attempt(attempt_key stored before submission)
submit; attempt state: SUBMITTED
attempt outcomes:
rejected / final failure -> next candidate immediately
delivered, objective not yet met -> wait until this attempt's own
deadline, then next candidate
no status by attempt deadline -> AMBIGUOUS; advancing is a decision
to accept possible duplication
recipient action -> journey COMPLETED, cancel pending
on late callback:
record against attempt; update attribution; never re-open a terminal journey
on source state change:
re-evaluate stop conditions immediately, regardless of pending attempts
The AMBIGUOUS branch is where most designs are silently wrong. Whether to advance without a status is a per-journey policy decision, and for anything where duplication is harmful — a second authentication code, a second payment request — the answer is usually to wait or stop rather than to send again.
Attribution and cost
Attribute the outcome to the journey, and record which attempt was in flight when the outcome occurred. Then report:
- Outcomes per journey type, and the share reached at each position in the chain.
- Cost per completed outcome across the whole chain, not per channel.
- Duplication rate: journeys where more than one attempt delivered.
- Ambiguity rate: attempts that ended with no final status.
- Stop-condition effectiveness: journeys where later attempts were correctly cancelled.
A chain whose second and third positions carry most of the outcomes is telling you the first position is wrong for that population.
Where the supply model fits
Flowstates sells and operates messaging routes across channels, supports customers keeping their own provider contracts (BYOV), and supports hybrid arrangements. The chain logic — eligibility, deduplication, deadlines, stop conditions and attribution — is the same work in each case, and it belongs next to the business state that defines the objective.