Skip to main content

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_hours through GET/PATCH /api/v1/settings/compliance/quiet-hours. The default object supplies shared values, and a channel entry overrides it. All 15 channels default to enabled: 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 send gate evaluates the org configuration in one of three modes: ENFORCE blocks the send and returns the quiet-hours error; WARN records the blocked decision and lets the caller continue; PREVIEW returns the decision without logging a warning or sending anything. The HTTP preview endpoint always calls the existing check in PREVIEW mode, so it is a read of the same decision—not a reservation and not enforcement. See Quiet hours configuration for the write surface and the checker tool for the federal-window-only tool.

The org channel wire set

The org setting accepts this QUIET_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 decision follows this sequence:
  1. The service’s resolveRecipientTimezone first honors a valid caller timezone override. Without one, it resolves +14155551234 through 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.
  2. resolveEffectiveWindow merges the org channel entry with the org default and 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.
  3. The service evaluates the resolved local hour and weekday. If the recipient is outside the effective window, it computes next_allowed_at as 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.
  4. Re-run the preview immediately before dispatch. A preview does not reserve the recipient, and a changed tenant setting can change the result.
For example, if the number resolves to 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 calls invalidateOrgQuietHoursCache 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

Send phone (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_at sets the next poll time instead of a fixed backoff.
When 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 pass timezone_override or 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:
  1. 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).
  2. 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).
  3. The org channel gate — for 1:1 sends, and as the global fail- open / fail-closed posture when no campaign row is involved.
  4. The platform default — when no org or campaign window exists, the channel default kicks in only if you enabled that channel.
That precedence is exactly what the preview shows back to you: a blocked campaign send surfaces the narrower campaign window in 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:
  1. 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.
  2. 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.
  3. Preview hit. Call GET /api/v1/compliance/quiet-hours/preview for the recipient and channel. Treat allowed_now: false plus reason: outside_window or wrong_day as the timed result, and use next_allowed_at rather than calculating a delay. The send-time gate still runs when the message is dispatched.
The campaigns preflight is a different layer. It is an advisory readiness check for the campaign as a whole, while the quiet-hours preview evaluates one recipient and channel. A commerce or campaign preview route may use similar “preview” wording, but it does not turn the compliance endpoint into a launch gate. A preflight warning can coexist with an allowed recipient preview, and a recipient preview can be blocked even when the campaign has no other readiness warning.

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 smallest next_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; reading false 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 returns reason: 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: your timezone_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 smallest next_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

Assume ORBIT_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)

Miami recipient, Sunday, 8:30 PM:
The same verdict is a hold on the campaign path — schedule to 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:
A blocked WhatsApp response at 08:30 local:
The Meta channels (instagram, messenger, apple_messages, line) behave the same way — 09:00–21:00 once enabled, channel_disabled otherwise.

Email — enforced via org timezone

Because the email preview carries a phone, it resolves the timezone from the NANP area code just like SMS. On an actual email send the recipient is an address, so the gate falls back to the org default timezone, then the org unknown_timezone_policy. With the org default set to America/New_York:

Pass-through — email, fax, push, web_chat not evaluated

Read 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 is unknown_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