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

# Cross-schedule planner: operator walkthrough

> Spread concurrent campaigns across a shared hourly cap from the Campaigns hub, and read the plan against the controls that still evaluate at send time: recipient-local quiet hours, per-recipient frequency caps, the federal voice window, and state calling-window overlays.

# Cross-schedule planner: operator walkthrough

The cross-schedule planner on the Campaigns page spreads concurrent campaigns across one shared hourly cap and recommends a start time for each. This guide walks the planner itself, and then the part a plan cannot show you: how a recommended slot composes with the controls that still evaluate when a message actually sends. For the allocation model behind the plan, see [the cross-campaign send allocator](https://docs.orbit.devotel.io/concepts/campaign-allocator-model.md); for the org defaults the planner starts from, see [Settings → Campaigns](https://docs.orbit.devotel.io/guides/settings-campaigns-console.md).

> [!NOTE]
>
> The planner is advisory. Generating a plan never schedules, launches, or mutates a campaign, and every recommendation is re-checked by the live send gates at dispatch.

## Open the planner and build a plan

1. Open the **Campaigns** hub and open the planner with **Spread sends**.
2. Pick the campaigns to spread. Only campaigns that can still take a start time are listed: drafts, scheduled, and paused. Each row shows its remaining audience ("N to send"); a campaign with nothing left to send is listed but cannot be picked. The preview arbitrates up to 100 campaigns per request, so split a larger calendar into two plans.
3. Set **Window start** and **Window end**. The start cannot be in the past and the end must be after the start. The form opens on now to seven days out and pre-fills your org defaults from [Settings → Campaigns](https://docs.orbit.devotel.io/guides/settings-campaigns-console.md) where they are set; overriding them here never changes the org default.
4. Set **Global cap / hour**: the maximum messages per hour across the selected campaigns together. This is your organization's shared throughput budget, the same figure [frequency caps](https://docs.orbit.devotel.io/guides/frequency-caps.md) describe per contact but applied to total volume.
5. Optionally switch on **Quiet hours** and enter a start and end hour. Wrap-around windows work: 21 to 9 plans around evening and early morning. The hours are entered in *your* local time zone; a banner above the fields and a chip on the field row name that zone, and rule out UTC and any fixed workspace or campaign zone. The planner sends that zone's offset (`tz_offset_minutes`) with the request.

Click **Generate plan**. Each campaign row returns its **recommended start** (the first scheduled slot, offered as the `scheduled_at` to apply), the scheduled count, and any overflow that did not fit the window; the summary row shows total scheduled versus requested and the peak slot utilization against the cap. Applying a recommendation happens through the **Schedule** link on each row, which opens the campaign's own schedule path. Any edit to the form clears the rendered plan, so what you read always matches the inputs.

### What the planner plans around, and what it only warns about

The planner's two time controls and the cap behave differently at send time:

| Input | In the plan | At send time |
| - | - | - |
| **Quiet hours** (planner toggle) | Excludes the window's slots from the spread; wrap-around windows like 21:00 to 09:00 are supported. | The window that actually holds a send resolves per recipient: the campaign's row window, then the org fallback, then the platform default. See [per-campaign quiet hours](https://docs.orbit.devotel.io/guides/per-campaign-quiet-hours.md). |
| **Global cap / hour** | Bounds combined per-slot volume for the campaigns you selected. | Your org frequency posture still governs overall throughput; other campaigns you did not select draw on it too. |
| **Per-recipient frequency caps** | Not an input. The planner cannot see or arbitrate them. | Still evaluated per message: a recommended slot can still be rejected with `429 FREQUENCY_CAP_EXCEEDED`. |

Hold that last row in mind when you size a spread. The plan arbitrates the selected campaigns against one shared hourly cap; it has no view of per-recipient rolling caps, unselected campaigns, or state calling windows. A plan is a good starting schedule, not a delivery guarantee. The note under the planner's quiet-hours fields says the same in one line: the window shapes the plan only, and each recipient's own local quiet hours still apply at send time.

## What still evaluates at send time

A recommended slot is a proposal. When the scheduled send fires, it walks the same gates as any other send:

* **The federal voice window.** US voice campaigns and dialer calls send only inside 8 AM to 9 PM recipient local. This guard is platform-owned: no tenant setting and no planner slot moves it.
* **State overlays.** A set of states narrow the federal window further with earlier evening cutoffs and blocked-day rules; see [US state calling windows](https://docs.orbit.devotel.io/compliance/state-calling-windows.md) for the current map. A slot that is fine federally can still land in a state hold.
* **Quiet hours, recipient-local.** The planner's quiet-hours input shapes the plan in your zone; enforcement re-evaluates each recipient's own local window (row window, org fallback, or the 21:00 to 09:00 platform default), hour-granular and end-exclusive.
* **Per-recipient frequency caps.** Every send checks the caps you created; a send past its cap is rejected `429 FREQUENCY_CAP_EXCEEDED`. Because the planner cannot arbitrate these, two campaigns planned a day apart can still collide on a contact capped at one message per day.
* **Opt-out and suppression.** Suppressed recipients never receive a scheduled send, planned or not.

When a gate holds a send, the held record reports the reason and `next_allowed_at`, the recipient-local instant the gate reopens; `GET /api/v1/compliance/quiet-hours/preview` returns the same decision for one recipient before you send. For the cap half, `GET /api/v1/contacts/:id/cap-status` returns the live per-channel usage and when each cap resets, so you can read back exactly why a planned recipient rejected a send. For how the caps sit beside the when-controls in a compliance posture, see [frequency caps as a TCPA litigation-risk control](https://docs.orbit.devotel.io/compliance/frequency-caps-compliance-posture.md).

## Worked example: two campaigns, a wrap-around quiet window across a DST cutover, and a state overlay

**Setup.** Campaign A (2,000 recipients, high priority) and Campaign B (1,500 recipients, low priority), spread from Saturday to Monday. Global cap 500/hour. Planner quiet hours 21 to 9. The window crosses the US fall DST cutover, when clocks in observing zones repeat the 1 AM hour on Sunday.

1. **Plan.** The planner excludes the 21:00 to 09:00 slots in the zone you entered. Across the cutover, quiet hours are resolved on local wall-clock: the repeated 1 AM hour stays inside the quiet window rather than opening a surprise send slot, and the morning reopen happens once, at local 09:00. This is the same behavior the single-campaign guide describes in [quiet hours and the DST crossover](https://docs.orbit.devotel.io/guides/quiet-hours-dst-crossover.md); the planner's spread inherits it. Generate the plan: Campaign A claims the Saturday and Sunday morning slots to cap, Campaign B defers into later slots, and a slice of B shows as overflow because weekend capacity ran out.
2. **Apply.** Follow each row's Schedule link and apply the recommended start through the campaign's normal schedule path. Nothing about the plan binds the runtime.
3. **Dispatch, first hold.** Campaign B's 5 PM slot reaches a recipient whose number resolves to a state with an overlay that closes earlier in the evening than the federal window. The send is fine federally but lands in a state hold; the held record carries `next_allowed_at` at the overlay's reopen, and the campaign resumes on its own when the window opens. One hold like this needs no action: the runtime re-queues against `next_allowed_at`.
4. **Dispatch, second hold.** A run of holds pushes some of B's sends into the next day, where the contact has already taken Campaign A's message and sits at its per-day cap. The send is rejected `429 FREQUENCY_CAP_EXCEEDED` rather than delivered late over cap. Read the contact's position with `GET /api/v1/contacts/:id/cap-status`.
5. **Resolve.** The cap is tenant-owned, so the resolution is yours: widen B's spread window so holds clear before the cap day rolls, lower B's volume, exclude the capped segment, or adjust the cap rule itself per [frequency caps](https://docs.orbit.devotel.io/guides/frequency-caps.md). The planner is not the resolution path for a cap collision; it never sees per-recipient caps, and no plan can override one.

## When the planner refuses, and what to do

| What you see | Why | Resolution |
| - | - | - |
| "The window start can't be in the past." | A past start would produce recommended send times that can never fire; the API rejects it with a validation error too. | Move the start to now or later. A start within a few minutes of now is accepted. |
| "The window end must be after the window start." | An empty or inverted window has no slots to fill. | Correct the end; check your clock if you typed the window from two different zones. |
| "None of the campaigns you picked have recipients to send, so no plan can be generated." | Every selected campaign is at zero remaining volume; the API refuses such a selection instead of returning an all-zero plan. | Pick campaigns that still have recipients, or close out the finished ones. |
| Generate stays disabled with the cap at 0 | A zero shared cap gives every slot zero capacity, so nothing can be placed. | Set the cap to the throughput your org actually shares; the org default pre-fills a usable value. |
| A campaign row reads "no slot available" | Every eligible slot in the window is quiet or already full for that campaign. | Widen the window, raise the cap, or relax the quiet-hours input for this plan. |
| An overflow badge ("N won't fit the window") | Demand exceeds the window's capacity at this cap. | Widen the window, raise the cap, or accept that the campaign continues after the window ends; overflow is reported, never silently dropped. |

## See also

* [The cross-campaign send allocator](https://docs.orbit.devotel.io/concepts/campaign-allocator-model.md) — the model behind the plan.
* [Settings → Campaigns console](https://docs.orbit.devotel.io/guides/settings-campaigns-console.md) — where the planner's defaults live.
* [Per-campaign quiet hours](https://docs.orbit.devotel.io/guides/per-campaign-quiet-hours.md) — the row-level window that overrides the org fallback.
* [Quiet hours and the DST crossover](https://docs.orbit.devotel.io/guides/quiet-hours-dst-crossover.md) — local wall-clock behavior across a cutover.
* [Send-window optimization with `send_at_type`](https://docs.orbit.devotel.io/guides/message-scheduling-send-window-optimization) — shifting single sends into a window.
* [Frequency caps](https://docs.orbit.devotel.io/guides/frequency-caps.md) and [frequency caps as a litigation-risk control](https://docs.orbit.devotel.io/compliance/frequency-caps-compliance-posture.md) — the per-recipient layer the planner cannot arbitrate.
* [US state calling windows](https://docs.orbit.devotel.io/compliance/state-calling-windows.md) — the overlays that still hold a planned slot.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.