> ## 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.

# Message priority and traffic lanes: keep OTP traffic ahead of campaign bursts

> How Orbit splits each account's per-second send budget into a reserved transactional lane and a shed-first marketing lane, how the per-message priority field steers a send into the right lane, and what happens to the reservation during a Redis outage.

# Message priority and traffic lanes

One account often carries two very different kinds of traffic: OTP codes and
fraud alerts that must leave *now*, and campaign blasts that can tolerate a
second of shaping. Both draw on the same per-account throughput budget — so
without protection, a campaign burst can consume the whole budget in a second
and shed the OTP behind it with a `429`.

Orbit prevents that by splitting every account's throughput into **two
traffic lanes**. You steer a send with the optional `priority` field on the
send request; the platform resolves everything else by where the send came
from. This page covers how the lanes work, how priority maps onto them, where
the reservation is exact, and how the lanes compose with the rest of the
[send gates](/concepts/send-gating-and-quiet-hours).

## The two lanes

Every send meters against one shared per-account counter — a per-second
budget (100 messages/second by default, adjustable on the account). The two
lanes differ only in how much of that shared budget they are allowed to
reach:

| Lane              | Who it is for                                                                 | Ceiling against the shared budget             |
| ----------------- | ----------------------------------------------------------------------------- | --------------------------------------------- |
| **transactional** | OTP, 2FA, fraud and security alerts — latency-sensitive, must-deliver traffic | The full budget                               |
| **marketing**     | Bulk and campaign traffic that can tolerate being shed first                  | `budget − reserve` (a 20% reserve by default) |

Both lanes increment the **same** counter; the lane only changes the ceiling
a send is allowed to reach. With the default 100 msg/s budget and 20%
reserve, marketing traffic tops out at 80 msg/s — the remaining 20 msg/s are
reachable **only** by transactional sends. A campaign blast can saturate its
own lane and get shaped; it can never consume the slice an OTP depends on.

A send with no recognizable classification resolves to the transactional
lane (full budget), so integrations that never think about lanes behave
exactly as before.

## The `priority` send field

Pass `priority` on any send endpoint (`POST /messages/sms`,
`/messages/whatsapp`, and the rest of the channel surface) to steer the
message explicitly:

| `priority`           | Lane                                                | Use for                              |
| -------------------- | --------------------------------------------------- | ------------------------------------ |
| `"high"`             | **transactional** (reserved)                        | OTP, 2FA, fraud / security alerts    |
| `"low"`              | **marketing** (shed-first)                          | Bulk blasts, newsletters, promotions |
| `"normal"` (default) | No override — falls back to origin-based resolution | Everything else                      |

When `priority` is `"normal"` or absent, the lane resolves by **where the
send came from**:

* **Direct API / SDK sends** → transactional lane.
* **Campaign- or journey-origin sends** → marketing lane.

An explicit lane hint always wins. If you already set
`metadata.traffic_lane` (`"transactional"` or `"marketing"`) on the request,
that value takes precedence over both `priority` and origin resolution — so
a campaign batch you have deliberately pinned to the transactional lane
(e.g. appointment reminders sent through the campaign tooling) keeps its
lane.

OTP sent under a campaign burst — the case the lanes exist for:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/messages/sms \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155552671",
    "from": "+14155551234",
    "body": "Your login code is 482-913. It expires in 5 minutes.",
    "priority": "high"
  }'
```

A bulk blast, politely shed-first:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/messages/sms \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155552671",
    "from": "+14155551234",
    "body": "Weekend sale: 30% off until Sunday.",
    "priority": "low"
  }'
```

### Scenario: OTP during a campaign burst

Assume the default 100 msg/s budget with a 20% transactional reserve.

| Second | Campaign traffic                       | OTP traffic                        | Outcome                                                                         |
| ------ | -------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------- |
| 1      | 250 sends/s offered, `priority: "low"` | —                                  | 80 msg/s dispatch; the rest get `429 RATE_LIMIT_EXCEEDED` with `Retry-After: 1` |
| 1      | Same burst ongoing                     | 15 OTP sends/s, `priority: "high"` | All 15 dispatch — the reserved 20 msg/s slice is untouched by the campaign      |
| 2      | Campaign retries arrive                | OTP subsides                       | Retried campaign sends dispatch as the marketing lane frees up                  |

Without the lanes, the 250-send burst would have swallowed the entire
budget and shed the OTPs alongside the promotions. With them, your retry
loop for campaign traffic (`wait 1 second, resubmit`) never touches OTP
latency.

<Note>
  Priority shapes **rate and queue order only**. It never changes where a
  message terminates, and it is not a delivery guarantee — a lane dispatch
  still depends on the downstream carrier accepting the traffic.
</Note>

## Cluster-correct reservation (and the Redis-outage behavior)

The per-second window is enforced with an atomic counter in Redis
(`INCR` with a 1-second expiry), shared across every API and worker replica
— so 100 msg/s means 100 msg/s **cluster-wide**, not per pod. Because both
lanes meter against that one shared counter, the transactional reserve is
**exact** on the Redis path: there is no replica-local drift that could let
a burst overrun the reserved slice.

If Redis is unreachable, the limiter deliberately falls open to a per-pod
in-memory bucket so sends never stop on an infrastructure blip. During that
window the lane ceilings still apply, but per pod rather than
cluster-wide — the reservation degrades to a best-effort lower ceiling for
marketing until Redis returns. A counter metric
(`devotel.messaging.rate_limiter.fallback`) records every fallback decision
so the degradation window is visible to operations.

## How priority composes with the other send gates

Lane shaping is one gate in the admission chain, not a replacement for the
others. A `"high"`-priority OTP still walks every gate you enabled — the
lane only decides **how much of your throughput budget** the send may
claim:

* **Opt-outs and suppression lists** still refuse the recipient first — no
  priority ships a message to someone who opted out.
* **Frequency caps and quiet hours** still apply — a high-priority send
  outside your quiet-hours window is blocked and reports
  `next_allowed_at` the same as any other send.
* **Throughput ceilings** (per-service and per-number caps) still meter
  independently of the tenant budget — they stack on top of the lanes.
* **Billing flags** still pause the whole account regardless of lane.

When a marketing send is shed by its lane, the response is
`429 RATE_LIMIT_EXCEEDED` with `Retry-After: 1` and a `traffic_lane: "marketing"`
detail — the same `wait-one-second-and-retry` posture as every other
throughput ceiling. See the
[rate-limit and cooldown taxonomy](/concepts/rate-limit-and-cooldown-taxonomy)
for the full map of limiter families and their error codes, and
[send gating and quiet hours](/concepts/send-gating-and-quiet-hours) for
the admission order a send walks before dispatch.

## See also

* [Outbound send gating](/concepts/send-gating-and-quiet-hours) — the full gate chain and what each refusal looks like
* [Rate-limit and cooldown taxonomy](/concepts/rate-limit-and-cooldown-taxonomy) — every limiter family and its error code
* [Voice & messaging pricing and throughput](/guides/voice-messaging-pricing-throughput) — per-service and per-number throughput ceilings
* [Rate limits](/guides/rate-limits) — per-endpoint API request limits and retry patterns
