> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Cascade (waterfall) fallback chains in the notify composer

> Build a per-recipient cross-channel fallback chain in the Messages → Multi-channel notify composer, pick between fan-out and cascade (waterfall) strategies, bound the escalation window, and read the chain preview before you send.

# Cascade (waterfall) fallback chains in the notify composer

`POST /api/v1/notify` sends one body to a mixed list of recipients across SMS, WhatsApp, email, RCS, push, Viber, Telegram, and Messenger in a single API call. The Messages → **Multi-channel notify** page in the dashboard is a visual composer over that endpoint, so you can build the recipient list and pick how it delivers.

This guide covers the two delivery strategies the composer offers, how cascade groups your rows into a per-recipient fallback chain, how to bound the escalation window, and how to read what came back. Everything here is a tenant-owned control — the chain order and the window are choices you make per message.

## Fan-out vs cascade (waterfall)

The composer asks you to pick one of two strategies. They differ in when each channel fires:

* **Fan-out (default).** Every recipient row sends immediately, in parallel. One delivery attempt per row; there is no retry-across-channels. Use it for general announcements where one channel — maybe two for redundancy — is enough and you want the message out everywhere at once.
* **Cascade (waterfall).** Only the first channel in each recipient's chain fires now. The remaining rows ride along as an ordered fallback chain, and the delivery engine advances to the next hop only when the active hop reports back undelivered inside the freshness window. Use it for time-critical messages (a one-time passcode, an appointment reminder, an outage alert) where the *cheapest reliable channel* should win and the expensive ones are a safety net, not a scatter-blast.

Order the chain richest → cheapest (e.g. RCS → WhatsApp → SMS → email). The row order on screen **is** the escalation order.

## Build a cascade in the composer

1. Open **Messages → Multi-channel notify** and click **Multi-channel notify** to open the composer.
2. Set the **Delivery strategy** to **Cascade (waterfall)**. The composer relabels the recipient list to "Recipients & chain".
3. Enter one recipient per row (channel + address). To give a recipient a fallback chain, **enter the same address on two or more rows** with different channels — rows that share an address merge into one chain. The leftmost column numbers each row so the escalation order is visible.
4. (Optional) Set the **Escalation window (seconds)**. This bounds how late an undelivered delivery receipt (DLR) may still trigger the next hop: minimum 30 seconds, maximum 24 hours. Leave it blank to use the 24-hour default. A value outside that range is dropped when the payload is built, so a mistyped window never blocks your send — and the API would clamp it to the floor/ceiling anyway.
5. Read the **per-recipient chain preview** under the list. Well-formed chains (2+ hops) render as `EMAIL → SMS → WHATSAPP`. A recipient entered once (or a duplicated address with only one channel) shows an amber **"Add another channel for failover"** callout, and the composer blocks the send with an error naming the offending address while you fix it.
6. Finish writing the body, optionally a sender, and click **Start cascade**.

The preview exists so a mistyped address or a single-hop recipient is visible **before** dispatch, not as a silent no-fallback chain.

### The single-hop guard

Cascade without a second hop degrades silently to fan-out semantics: the first hop fires and there is nothing left to escalate to. The composer therefore requires **at least two channels per recipient** in waterfall mode and blocks the send otherwise. (The API's own refine only requires ≥2 bindings total for waterfall mode — the per-recipient check here is stronger and lives in the composer.) When you build payloads in code rather than the composer, apply the same rule yourself.

## The API the composer calls

Both quirks are sent on `POST /api/v1/notify` — the strategy as `mode`, the window (waterfall only) as `fallback_window_seconds`:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/notify/" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "waterfall",
    "fallback_window_seconds": 300,
    "body": "Your appointment is for 10:30 AM tomorrow.",
    "from": "CareClinic",
    "bindings": [
      { "channel": "email",    "address": "to@customer.example" },
      { "channel": "sms",      "address": "to@customer.example" },
      { "channel": "whatsapp", "address": "to@customer.example" }
    ]
  }'
```

`mode` defaults to `fanout` when omitted; `fallback_window_seconds` accepts 30–86400 and defaults to 86400 (24h). Every hop still runs through the messaging router — sender resolution, opt-out, and compliance checks apply per hop, matching each destination's reachability rules. The full field specification (per-binding overrides, shared `template_name` / `template_params`, response envelope) is in the [/notify endpoint reference](/api-reference/endpoints/messaging).

## Worked example: an `email → SMS → WhatsApp` OTP reminder

An OTP flows exactly the same as the reminder above. Pick the channels by cost and reach — `email → SMS → WhatsApp` in this case:

| Hop | Channel    | Why it's here                                                                  |
| --- | ---------- | ------------------------------------------------------------------------------ |
| 1   | `email`    | Cheapest hop that delivers to everyone with an app/email client. Try it first. |
| 2   | `sms`      | Reaches phones without an app and without a data connection.                   |
| 3   | `whatsapp` | Higher open rate than SMS where the customer has WhatsApp; ends the chain.     |

Each hop is independently delivered against the per-channel validators, opt-out lists, and quiet-hours rules your tenant owns. The first hop that delivers ends the chain — later hops never fire.

## Reading the result

The screen tells you whether each row queued and whether the strategy badge says **Fan-out** or **Cascade**:

* **200 OK** — every binding succeeded (fan-out), or at least one binding queued (waterfall). The row list shows a per-row chip: **Queued (fan-out)** — it fired now; **Queued (waterfall)** — this hop is the one that fires first, and **any** delayed deliveries for still-pending hops have not happened yet.
* **207 Multi-Status** (fan-out only) — some rows queued and some failed. The row chips show **Failed** with a human-readable reason (opt-out, quota, bad address); hover a row to see the machine-readable code. Branch per row — never blanket-retry the batch.
* **422** — in fan-out every binding failed; in waterfall every binding was rejected synchronously. Nothing was queued; fix the body/addresses and try again.

The composer's summary strip counts Total / Queued / Failed, and in cascade mode adds the note "**Only the first hop per recipient is firing now** — the rest escalate automatically if it comes back undelivered." A queued cascade row is **not** a delivered message yet; have your webhook subscriber for the delivery-event stream treat those as armed, not settled.

<Note>
  The dashboard badge "cascade queued — only the first hop fired" means exactly that. A queued row next to an overwrite from an earlier run is fine; treat delivery (confirmed DLR) as the terminal signal, and use fan-out when you cannot tolerate an ambiguous queued-only result.
</Note>

## When to use which — a decision shortcut

| Message shape                                                        | Strategy                                    |
| -------------------------------------------------------------------- | ------------------------------------------- |
| Blast one announcement to everyone (newsletter, maintenance window)  | **Fan-out**                                 |
| OTP / verification code, appointment reminder, outage alert          | **Cascade** with `email → sms → whatsapp`   |
| Broadcast with expensive channels and a defined budget per recipient | **Cascade** with a bounded window (30s–24h) |
| Broadcasting to a list where ordering doesn't matter                 | **Fan-out**                                 |

## See also

* [/notify endpoint reference](/api-reference/endpoints/messaging) — full request/response specification for both strategies.
* [Verify fallback chains](/guides/verify-fallback-chains) — ordered channel fallbacks for OTP verification, as a reusable profile rather than a per-message chain.
