Voice Guard: the Federal Dialing-Window Decision Check
Outbound voice dispatch in Devotel Orbit passes one recipient-level decision check before the call is placed: is this recipient reachable under the US federal TCPA window — 8 AM to 9 PM recipient-local time — at this moment? This page documents that check: the gates it runs at, how the verdict is computed, the error surface a blocked dial returns, and what distinguishes this guard from your tenant-owned quiet-hours settings. The worked captures at the end walk eachreason through the
correction path.
1. Where the voice firewall is deployed
The guard is wired at the dispatch decision point, not the campaign definition. Every outbound voice dispatch channel consults it before placing the call:- The public voice-dispatch API — every
POST /voice/calls-class dispatch request. - Dialer pacing workers — predictive, progressive, preview, and agentless campaign dials.
- Callback dispatch workers — virtual-hold and scheduled callbacks.
- The voice gateway pre-dial gate — the final hop before the carrier egress.
2. How the gate evaluates
The decision is a short, deterministic chain. Non-+1 recipients and non-NANP numbers skip the federal window entirely; everything else is evaluated against recipient-local time.- Recipient check — the guard reads the E.164 destination. A
non-US, non-NANP recipient is allowed immediately (reason
non_us_recipient); tenant quiet-hours or state overlays may still apply downstream, but the federal wire is skipped. - Timezone resolution — for a +1 recipient the guard resolves the
timezone from the NANP area-code map. If no map entry resolves, the
check fails closed: because TCPA jurisdiction cannot be disproved,
the dial is denied rather than guessed (reason
timezone_unresolved). - Local-hour evaluation — the resolved timezone converts the evaluation instant into a recipient-local hour. An unresolvable hour fails closed the same way.
- Window comparison — 8 AM to 9 PM recipient-local is allowed
(reason
inside_federal_window); anything else is denied, and the result carries the UTC instant the window next opens (next_allowed_at), computed with DST-safe hour arithmetic.
3. What a hard block means
A blocked evaluation in enforce mode returns HTTP422 with the error
code TCPA_FEDERAL_DIALING_WINDOW_BLOCKED. That code is intentionally
distinct from TCPA_DIALING_WINDOW_BLOCKED (your tenant quiet-hours
gate), so audit filtering can separate federal-window denials from
org-configured window denials.
The error body carries a structured detail object so your retry logic
never has to parse message text:
The
reason enum:
4. How this differs from your tenant-owned controls
Every other compliance gate — tenant quiet-hours, DNC/consent, country blocks, the orgunknown_timezone_policy (skip / enforce_utc /
deny) — is something you own: it is configurable per tenant, defaults
open, and fails open when resolution is impossible.
This guard is the one exception. It accepts no tenant toggle and no
per-tenant opt-out, and its timezone-unresolved path always fails
closed regardless of your org policy setting. The federal
$500–$1,500-per-call penalty (47 U.S.C. § 227(b)(1)(B)) is the reason
that asymmetry exists: the guard checks one input (the recipient’s NANP
timezone) and owns one window (8 AM–9 PM), independent of anything you
configure. Any override posture — for example a tenant state-list
exception — belongs in the tenant quiet-hours layer, never here; this
gate has no exemption knobs.
State overlays still matter, but they intersect downstream: the
state calling windows overlay can
only narrow within the federal window (e.g. Florida’s 8 PM close); it
never reopens a federally closed hour. The relationship is spelled out
on quiet-hours preview.
5. Worked captures
The three captures below cover the error body one reason at a time: the blocked-window capture as it lands across a DST switch, the fail-closed capture on a recipient the map cannot resolve, and the same field set on an allowed verdict so you can readdetails.reason even when nothing
was blocked.
outside_federal_window across a DST switch
A campaign dial targets +1 305 555 0101 — America/New_York per the
area-code map — in the week of the November 1, 2026 switch back to
standard time. The dispatch is evaluated at 22:40 recipient-local on
Friday, October 30 (still on daylight time), so local hour 22 is
outside 8–21 and the enforce-mode dispatch receives:
next_allowed_at accounts for it:
Saturday, October 31 at 8:00 AM daylight time is UTC 12:00, but had the
evaluation landed after the switch — the same 8:00 AM opening read as
standard time — the opening would be UTC 13:00 instead. The field
already carries whichever offset applies at the opening instant, so
schedule against next_allowed_at and never re-derive the opening hour
in your own scheduler with a fixed UTC offset.
timezone_unresolved fail-closed
A campaign list carries a Mexican recipient, +52 55 5555 0101. The
guard only reads NANP area codes, so a non-+1 recipient that reaches the
map by mistake — for example a contact record whose country code was
mis-set to a Mexican E.164 — resolves to no timezone entry, and the
check fails closed:
timezone, local_hour, and
next_allowed_at are all null, because there is no resolved zone to
evaluate and no next opening to compute. The correction is to fix the
contact record’s timezone — or remove the non-NANP recipient from the
list — then re-dispatch; without that correction every retry fails
closed identically. The same fail-closed posture applies to genuine +1
recipients the map cannot resolve (Canadian and Caribbean +1 area
codes) — setting the contact’s timezone resolves those.
transactional_lane on an allowed verdict
The guard also surfaces verdicts that allow the call, and the structured
fields come along — a warn- or preview-mode evaluation reads:
details.reason before planning, even when the call was not
blocked: transactional_lane says the call’s surfacing (a live agent
dialing one number, a returned call, an account or delivery
notification) placed it outside the federal window’s statutory scope, so
no next_allowed_at is computed. That is still the one check result the
platform will stand behind: an absent or unknown lane evaluates as
marketing, so an allow like this one never comes from a lane you
neglected to set — it comes from the attribution the platform derived.
Tenant quiet-hours and DNC/consent gates still evaluate downstream; the
allow verdict only clears this gate.
The correction path
Readreason first, then act on it:
outside_federal_window— schedule the dial tonext_allowed_at. Queue workers re-poll on that instant; a campaign launch holds to the smallestnext_allowed_atacross recipients.timezone_unresolved— the recipient resolved to no timezone entry (a non-NANP E.164, or an unmapped Canadian/Caribbean +1 area code). Fix the contact record’s timezone, then re-dispatch; without the correction every retry fails closed identically.non_us_recipient/inside_federal_window/transactional_lane— no action; the verdict is advisory for downstream gates only.
See also
- Quiet-hours preview — the read-only “would this dispatch now” endpoint across channels
- US state calling windows — the mini-TCPA overlays that narrow the federal window
- TCPA posture across SMS and voice — the tenant-owned control map
- Troubleshooting compliance error codes —
the full error-code index including both
TCPA_FEDERAL_DIALING_WINDOW_BLOCKEDandTCPA_DIALING_WINDOW_BLOCKED