Skip to main content

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 example at the end walks a flagged +1 recipient 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.
All four consult the same shared decision module, so a verdict is identical at every gate — there is no path that skips the check. Every evaluation, allowed or blocked, emits an observability event, so monitoring the gate’s wire-up is a count question, not a code question. Blocked evaluations log with the recipient number masked.

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.
  1. 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.
  2. 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).
  3. Local-hour evaluation — the resolved timezone converts the evaluation instant into a recipient-local hour. An unresolvable hour fails closed the same way.
  4. 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.
The guard supports three evaluation modes at the integration points that ask for a decision without enforcing it: enforce (the default — a block denies the dial), warn (returns the verdict and logs it, for dry-run pacing), and preview (returns the verdict silently, for scheduling previews).

3. What a hard block means

A blocked evaluation in enforce mode returns HTTP 422 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 org unknown_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 500500–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 example: a +1 recipient gets flagged

A campaign dial targets +1 305 555 0101 at 22:40 recipient-local (11:40 PM at the evaluation instant — before the dialer’s batch spins up): the area-code map resolves America/New_York, local hour 22 is outside 8–21, and the enforce-mode dispatch receives:
The correction path reads the reason first:
  • outside_federal_window — schedule the dial to next_allowed_at. Queue workers re-poll on that instant; a campaign launch holds to the smallest next_allowed_at across recipients.
  • timezone_unresolved — the recipient’s area code resolved to no timezone entry (common on Canadian or Caribbean +1 numbers). Fix the contact record’s timezone, then re-dispatch; without the correction every retry fails closed identically.
  • non_us_recipient / inside_federal_window — no action; the verdict is advisory for downstream gates only.
Because next_allowed_at is UTC ISO and DST-safe, schedule against that field and never re-derive the opening hour in your own scheduler.

See also