Quiet-Hours Preview: Channel-by-Channel Semantics
GET /api/v1/compliance/quiet-hours/preview answers “would this send
to this recipient, on this channel, dispatch right now?” — as a
read-only check. This page walks the response field by field, then
per channel: where voice splits between the always-hard campaign
path and the advisory ad-hoc path, how email picks a timezone
when the recipient is an address rather than a phone number, and the
stable SMS and Meta-channel defaults. The worked calls and the
troubleshooting table at the end cover the conditions that decide
what you do with a blocked verdict.
All examples are rooted at
https://api.orbit.devotel.io/api/v1/compliance.
1. Preview vs. the send gate — advisory check, not enforcement
The preview is a read on the same check the send path enforces — it never dispatches, never logs to the recipient, never suppresses. The send gates remain the enforcement layer: a send that passes preview can still be held at dispatch when a second gate (DNC, spend cap, provider state) intervenes; a send that fails preview can still dispatch when the gate is configured fail-open for that channel. Read the preview as a planning signal, and treat the gate at send gates as the verdict that actually holds or releases traffic. That advisory/enforced split matters when you wire the preview into a scheduler: polling the preview before a batch launch is cheap and correct; relying on it as the only gate is wrong — the send path must still go through the enforced gate on every dispatch. The tenant-owned framing stays the same here as everywhere on this site: Orbit is the conduit and the ledger — the gate is yours to configure, the preview is yours to consult, and you own the decision to schedule or dispatch. Nothing on this page imposes a global default beyond the US federal voice window (see Section 3).Configuration: the org gate, campaign fallback, and modes
Quiet hours has two tenant-owned configuration surfaces. Keep them separate when you troubleshoot a result:- Org-level channel gate: configure
organizations.settings.quiet_hoursthroughGET/PATCH /api/v1/settings/compliance/quiet-hours. Thedefaultobject supplies shared values, and a channel entry overrides it. All 15 channels default toenabled: false, so this gate is off until you opt in. - Campaign-level fallback: configure
PUT /api/v1/campaigns/quiet-hours/settings. This one start/end window applies only to drip and journey sends whose campaign row does not provide its own window. It does not replace the org gate, and it cannot widen an enabled org channel.
The org channel wire set
The org setting accepts thisQUIET_HOURS_WIRE_CHANNELS set. A channel is evaluated only after you enable it; an omitted or disabled entry returns channel_disabled and remains allowed.
The org setting supports partial per-channel objects:
enabled, start_hour, end_hour, and optional allowed_days (0 Sunday through 6 Saturday). Missing values inherit from default, then the channel’s platform default. This is why enabling sms alone uses the standard SMS window without copying every field into the request.
A complete “would this send go through?” check
Use a caller-supplied E.164 number when you want the preview to mirror a real recipient:- The service’s
resolveRecipientTimezonefirst honors a valid caller timezone override. Without one, it resolves+14155551234through the NANP area-code mapping, then the country-prefix map. Use Number timezone and state resolution when the inferred zone does not match the recipient’s actual location. resolveEffectiveWindowmerges the org channel entry with the orgdefaultand the channel platform default. For a campaign, the campaign row can narrow this result, followed by the campaign fallback when the row has no window.- The service evaluates the resolved local hour and weekday. If the recipient is outside the effective window, it computes
next_allowed_atas a DST-safe UTC instant. Schedule to that ISO value; do not add a fixed number of hours, because a daylight-saving transition can make a local night 23 or 25 hours long. - Re-run the preview immediately before dispatch. A preview does not reserve the recipient, and a changed tenant setting can change the result.
America/Los_Angeles and the org window is closed at the current local time, the response includes allowed_now: false, the resolved local_timezone, the effective window_start_local and window_end_local, and the next DST-safe next_allowed_at. If the channel is disabled, it returns allowed_now: true with reason: channel_disabled instead of applying a window.
Unresolved timezone: tenant gate vs. federal guard
For tenant-controlled quiet hours, an unresolved timezone fails open by default:unknown_timezone_policy: skip returns allowed_now: true with reason: unknown_timezone_skip. Set enforce_utc to evaluate a deterministic UTC window, or deny to hold unresolved recipients. This is the tenant’s control and defaults open, as described in the compliance rules.
Do not apply that rule to the platform-global TCPA federal voice guard. For US campaign and dialer voice, the guard is the sole global hard gate and fails closed when the recipient timezone cannot be resolved; no org toggle, WARN/PREVIEW mode, or consent setting bypasses it. The US calling windows page explains that federal path. The checker tool is useful for that federal baseline, but it does not show your org-level configuration.
Cache behavior after an org edit
The service caches each organization’s parsed quiet-hours document in-process for 60 seconds. When the org settings write succeeds, it callsinvalidateOrgQuietHoursCache for that organization, removing the entry immediately in that API process. Other replicas have their own process-local entry, so allow up to the 60-second TTL there; re-run the preview after the write and again if a replica still reports the previous window. The cache is a performance layer only: it does not change the tenant-owned default-off posture or the federal voice guard.
2. The preview response
Sendphone (E.164) and channel (required), plus an optional
timezone_override (IANA name) when your CRM knows better than the
area code. The response is a decision snapshot.
The
reason enum:
The blocked trio at the bottom is the set that needs a retry plan.
allowed_now is true on every allowed reason.
3. Interpreting next_allowed_at — requeue vs. schedule
Read next_allowed_at as “the earliest instant this send would pass
on this channel for this recipient,” not as a wall-clock delay. Two
consumer patterns make the distinction concrete:
- Scheduled sends (campaign launches, dialer batches): persist the instant and let the campaign scheduler or dialer pacing loop wake at it. The DST-safe ISO means a 23- or 25-hour night in the recipient’s zone does not drift the schedule — never re-derive hours in your own scheduler arithmetic.
- Requeue (a queue worker polling per recipient): stash the instant
on the job as a delayed-until, re-poll at that instant, and drop the
job to a “hold” bucket until it passes. A per-job
next_allowed_atsets the next poll time instead of a fixed backoff.
allowed_now is true, next_allowed_at echoes the evaluation
time — the field is always present so you never branch on absence.
Conversely, when local_timezone is null (pass-through channels),
the ISO timestamp is still meaningful: it is just “now.”
Pair the timestamp with the reason before you act on it:
outside_window/wrong_day— the timestamp is a deterministic reopen; schedule to it.unknown_timezone_deny— the timestamp is “now,” but the send will keep failing until you supply a timezone; the right move is to passtimezone_overrideor fix the contact record, not to schedule.
4. Drip/journey sends vs. 1:1 sends — which window wins
The preview evaluates one recipient against one window, but your sender-side posture differs for bulk traffic (drips, journeys, campaigns) and 1:1 traffic (dashboard, API). The resolution order for a campaign-dispatched send is:- The campaign row’s own window — if the campaign row carries
quiet_hours_start/quiet_hours_end, the preview applies those bounds (and only ever narrows the channel window). - The org campaign-fallback window — when the row is silent, the
org-level fallback applies (Settings → Campaign limits → Default
quiet hours, or
GET/PUT /api/v1/campaigns/quiet-hours/settings). - The org channel gate — for 1:1 sends, and as the global fail- open / fail-closed posture when no campaign row is involved.
- The platform default — when no org or campaign window exists, the channel default kicks in only if you enabled that channel.
window_start_local / window_end_local, not the org gate’s bounds.
Check your campaign window before assuming the org gate is the
binding layer.
A 1:1 send skips the campaign rows entirely and reads only the org
channel gate; a campaign send intersects the campaign window with the
channel gate. Frame the preview call accordingly — the same phone
and channel pair can yield different bounds depending on which
layer is in play.
Which layer answered a timed block?
Use this decision tree when a campaign launch and a recipient preview do not appear to agree:- Org-clipped gate. Start with the channel gate owned by your organization. If it is enabled, its hours are the outer boundary for the preview. A campaign may narrow that boundary, but it cannot widen it.
- Campaign fallback. For a campaign recipient, read the campaign row’s window first. If the row has no window, resolve the organization campaign fallback. This fallback supplies bounds; it does not pause a launch or hold a message by itself.
- Preview hit. Call
GET /api/v1/compliance/quiet-hours/previewfor the recipient and channel. Treatallowed_now: falseplusreason: outside_windoworwrong_dayas the timed result, and usenext_allowed_atrather than calculating a delay. The send-time gate still runs when the message is dispatched.
Timed errors: authorization is not a quiet-hours verdict
Separate resource access errors from timing errors before deciding what to retry:
The preview endpoint itself is a read: it returns a decision snapshot and
does not pause a campaign. A secondary campaign fallback also only
supplies the window used for that snapshot. Only the enforced send gate can
hold a dispatch, so do not interpret a successful preview as a reservation
or a blocked preview as proof that every recipient in the campaign is
blocked.
Launch timing and state overlays
For a campaign launch, use the smallestnext_allowed_at across the
recipient set only when your launch policy is to wait for the whole set.
Otherwise hold the blocked recipients and continue the channels that are
allowed. Re-preview at that instant: an allowed time is not a reservation,
and a change to the tenant-owned window can change the result before the
send.
For US voice recipients, the federal 8:00–21:00 local window is further
intersected by the applicable state mini-TCPA overlay. The overlays include
Florida, Mississippi, Oklahoma, Louisiana, Alabama, Arkansas, and West
Virginia; each can shorten the local window or restrict a day. A state
overlay does not change the read-only nature of preview: it changes the
window_*, reason, and next_allowed_at values that the enforced voice
path applies. Keep those tenant-owned state settings aligned with your
launch policy and re-preview after changing them.
5. Voice — the two enforcement forks
Voice is the only channel where the owner of the verdict changes by path. The preview returns the window evaluation; readingfalse
differently per path is the whole point of this section.
Campaigns and dialers: hard block, always. US (+1) recipients on
campaign or dialer traffic — predictive, progressive, preview, and
agentless modes, scheduled callbacks — are refused outside the window
regardless of your tenant settings. There is no org opt-out on this
path; your quiet-hours toggle neither relaxes nor replaces it. When
allowed_now reads false, next_allowed_at is the instant your
campaign scheduler waits for.
Ad-hoc 1:1 dials: advisory until you opt in. A dashboard or
softphone dial outside the window dispatches with the decision logged.
allowed_now: false here is a caution you can override at the
dialer — unless you enabled the voice channel on your org gate via
quiet hours configuration, in
which case every outside-window dial holds until next_allowed_at.
US recipients get the federal window — intersected with state
overlays. The binding hour range is eight-to-nine recipient-local
intersected with any mini-TCPA overlay the area code maps to, e.g. a
Florida recipient closes at 8 PM and never dials on Sunday. The
non-US recipient skips the federal wire entirely; they hit your org
gate if opted in, or pass through. The overlay table and the
decision’s gate order are on
US state calling windows.
6. SMS and Meta messaging channels — the stable semantics
SMS, MMS, RCS, Viber, and Telegram default to 08:00–21:00 once you enable each channel; WhatsApp and the Meta channels (Instagram, Messenger, Apple Messages, LINE) default to 09:00–21:00. The window applies only when you enabled the channel on your org gate — a disabled channel returnsreason: channel_disabled and dispatches.
SMS also reads the campaign fallback window when the recipient is in a
campaign: the campaign row’s own window, then the org default fallback
window, then the platform defaults. Fallbacks only ever narrow the
channel window. Full mechanism on
quiet hours configuration and
TCPA posture across SMS and voice.
7. Email — DST-correct evaluation without a phone number
Email recipients are addresses, so the timezone resolution differs: yourtimezone_override hint, then the org default timezone, then the
org unknown_timezone_policy fallback — skip (default, fail-open),
enforce_utc, or deny.
The window applied is recipient-local hour math resolving to a UTC
instant. The detail of next_allowed_at — DST-gap straddles,
Arizona and Hawaii non-observance, integer-hour traps — lives on the
quiet-hours DST crossover walkthrough.
Schedule to that field; never re-derive hours in your own scheduler.
8. Where the preview result flows
Campaign pre-flight. Before a launch, pre-flight previews the recipient set per channel. A blocked preview pushes the launch to the smallestnext_allowed_at; the per-channel reasons tell you whether
to reschedule the whole campaign or hold the specific channels that
are blocked.
Schedule workers. Workers that fan a queue send against
recipient-local time poll the preview and hold until
next_allowed_at; the DST-safe ISO means a queue survives a 23- or
25-hour night without drifting (see the
DST crossover guide for the
allowed-hour math).
Dialer pacing. A pacing loop that batches recipient time zones
splits the queue into “dispatch now” and “hold” buckets on the verdict;
a per-bucket next_allowed_at sets re-poll instead of a fixed delay.
Dashboard and agent surfaces use the same read for a “would this go
through now” indicator, without burning a real send.
9. Worked calls
AssumeORBIT_API_KEY holds a live API key.
SMS — enabled, in window
SMS — enabled, blocked on hour
Same call at 22:00 local:Voice — campaign path hard block (state overlay)
next_allowed_at. On an ad-hoc dial it is advisory: the dial
dispatches unless your voice gate is on.
WhatsApp — Meta-class default window
WhatsApp defaults to 09:00–21:00 once the channel is enabled on the org gate:instagram, messenger, apple_messages, line)
behave the same way — 09:00–21:00 once enabled, channel_disabled
otherwise.
Email — enforced via org timezone
unknown_timezone_policy. With the org default
set to America/New_York:
Pass-through — email, fax, push, web_chat not evaluated
local_timezone before planning on push/fax/web_chat — a null
means the pass-through, not a failed resolution. Email/fax/push/web_chat
skipped because the platform considers them asynchronous surfaces whose
morning delivery doesn’t interrupt the recipient — but once you enable
one, channel_disabled stops appearing and the full contract applies.
10. Troubleshooting — odd next_allowed_at values
11. Edge cases
Recipient timezone unresolved. Campaign/dialer voice to a US recipient is fail-closed worldwide — the reason isunknown_timezone_deny and the plan is to pass timezone_override or
fix the contact’s timezone. Every other channel, and ad-hoc voice to
non-US recipients, defaults the policy to skip (allowed, reason
unknown_timezone_skip). Set the org policy to enforce_utc to
evaluate against UTC where UTC is the resolved local_timezone, or
to deny to hold.
Non-US recipient. The federal wire is skipped — the channel gate
applies if opted in, or channel_disabled passes. The country-prefix
map supplies a capital-city timezone for well-known markets, so
unknown_timezone_skip usually stays empty. A +1 NANP number that
resolves to no mapped area code (Canadian, Caribbean) fires the
unknown_timezone_deny on the voice campaign path.
Pass-through channels. channel_disabled means no window was
applied; the send dispatches. Enable one of
email/fax/push/web_chat and the semantics shift to resolved in
the section above.
Campaign fallback narrowing. SMS fallback windows can provide a
stricter window than the channel default (e.g. 09:00–20:00 on a
campaign row). A blocked preview on a campaign recipient surfaces the
campaign window bounds in window_start_local / window_end_local
— check your campaign window before assuming the org gate.
See also
- Send gates — the preview and the federal dialer asymmetry in the full gate stack
- US state calling windows — the overlay table, gate order, and the voice reading of the decision
- TCPA posture across SMS and voice — the tenant-owned control map and campaign fallback windows
- Quiet hours configuration — enable per channel, set fallback windows, set the unknown-timezone policy
- Quiet hours DST crossover — the pre-launch preview procedure and the two zones that drift
- Country rules enumeration model — the per-(country, channel) register the preview’s channel semantics sit beside — sender types, registration, DLR support, and how the rows feed the send-time gate