Blog

Two gates enforce outbound calling compliance

Outbound calling compliance at two chokepoints: the do-not-call list and calling hours are checked where every call passes, so nothing routes around them.

A schematic on black. Four blocks on the left labelled direct, campaign, schedule and lead, each with a lane running right into one tall block labelled gate that contains two smaller blocks, do-not-call list and calling hours. The campaign lane stops at the gate's edge with a short coral bar. The schedule lane leaves the gate dashed, passes through a hatched span, and rejoins. The direct and lead lanes pass through and merge with it into one arrow ending in a block labelled dial.
Bekir İşgörCo-founder
8 min read

Share

Updated 14 September 2026 with figures, how the rules changed over the summer and what production shows.

Outbound calling compliance on Talkif is not a setting: there is no way to place an outbound call that skips the do-not-call check. The platform has exactly two points at which a call is admitted — a direct API request, and the queue that every campaign, schedule, lead integration and retry passes through — and both consult the account's do-not-call list before anything is dialled. A number on the list gets a 422 contact_on_dnc_list from the API, or a campaign contact marked skipped with the reason recorded. Calling hours work at the same two points: a confirmed violation stops an automated call from going out until the recipient's window opens, and a direct call outside the window is allowed through and written to the audit trail. Between 14 August and 12 September 2026, production admitted eight direct calls outside their recipients' hours and refused none for do-not-call — small numbers, and the honest ones.

The do-not-call list is a list of numbers, not a flag on contacts

The do-not-call list is per account, and it is a list of phone numbers. A number on it is blocked whether or not it also exists as a contact, and however the call is started. Numbers go in as E.164 — + followed by country code and subscriber number — and are matched by exact equality against the dialled number. There is no fuzzy matching: a number imported with a stray space would never match a real dial, so the single-entry API validates the format on the way in.

Three things feed the list today: the do-not-call API (POST /dnc, one number at a time, with a source and a reason), a bulk import of up to 10,000 numbers in one request that reports how many were added and how many were already there, and the Do not call action on a lead, which adds the lead's number, cancels any call already scheduled for it and marks the lead suppressed. What the platform does not do is detect an opt-out inside the conversation, read incoming SMS for a stop word, or hold consent records. A stop request that reaches your inbox or your CRM is invisible to Talkif until you post it to the list, which is why the docs tell you to wire your unsubscribe path to that endpoint.

Two chokepoints cover every path

The question to ask of any compliance feature is whether there is a way around it. For outbound calls on Talkif there are two admission paths, and that is a fact about the architecture, not a policy.

A direct call — someone clicks Call in the dashboard, or your system posts to the calls API — is admitted by the call service, which checks phone ownership, then that the number is active, then the do-not-call list, before it looks at anything else. Everything that is not a direct call goes through the queue: campaign dials, scheduled calls, calls triggered by a Meta lead, retries of failed attempts, and direct calls that overflowed into the queue under capacity pressure. The queue processor runs the same do-not-call check on every item it claims. Because retries re-enter through the queue, a number added to the list in the middle of a campaign is blocked on its next attempt, not at the end of the run.

The result is that a flow author, a campaign setting or an integration cannot opt out of the check, because none of them ever see it. It sits below them.

A decision schematic: queue item, then on the do-not-call list? with three branches — listed to skipped, database error to fail open, count the error, which rejoins, and not listed to inside the recipient's window? — which branches inside to dial, or outside to a coral block held until the window opens, which loops back to the question.
Admission on the queue path. Two questions in series; a database fault is counted and let through, and a call outside its window is held, never dropped.

The list is the platform's half of an obligation the law puts on the caller. The FTC's guidance for sellers is that "A consumer whose number is not on the national registry can still prohibit individual telemarketers from calling by asking to be put on the company's own do not call list", and that a seller using the national registry "must synchronize their lists with an updated version of the registry at least every 31 days" (FTC, Q&A for telemarketers and sellers about DNC provisions). The account list is that company-specific list; the 31-day registry refresh is the bulk import, on your schedule.

What a blocked call looks like

A refused direct call is an HTTP 422 with the error code contact_on_dnc_list and a message telling the caller to remove the number from the list or skip the contact; there is no per-call override. In a campaign, the contact's status becomes skipped with the reason dnc_list, the campaign's skipped count goes up, and the dashboard shows the contact as skipped. The call record itself is marked failed with a policy reason, so the attempt is visible in call history rather than silently absent.

Inbound calls are never checked. If someone on your list rings you, the agent answers — a do-not-call list is about calls you place.

A state schematic of a campaign contact: pending to queued, claimed; queued to held, outside the window, and back, window opens; queued to dialled, admitted; dialled to completed, answered; dialled back to queued, no answer, attempts left; dialled to exhausted, attempts used up; and a coral arrow from queued to skipped, on the do-not-call list.
A campaign contact's states. Held and skipped are the two things the gate can do to a contact; everything else is the ordinary life of a dial.

Fail open, and alert on it

When the database that holds the list cannot be reached, the check fails open: the call proceeds. This is a deliberate choice, and the reasoning is written into the code. A temporary database fault should not stop every outbound call on the platform, and the list is a layer of defence, not the whole of it — the customer, not the platform, is ultimately responsible for who they call. The error path is counted separately, and the recommended alert is that any non-zero rate of check errors means the fail-open branch is firing and the database needs attention.

Calling hours: a floor set by law, and a timezone worked out from the number

The US rule is precise. The FCC's regulation says that "No person or entity shall initiate any telephone solicitation to: (1) Any residential telephone subscriber before the hour of 8 a.m. or after 9 p.m. (local time at the called party's location)" (47 CFR § 64.1200(c)). The hard part is in the parenthesis: the window is in the recipient's local time, and a phone number is the only thing you know about the recipient.

Talkif keeps a table of calling-hours rules keyed by country, with a US row at 08:00–21:00 that cites that regulation. Until July 2026 the US row was the only one, which meant a number in any other country fell through to no rule at all and could be dialled at 3 a.m. Since 8 July every country without its own row gets a default 08:00–21:00 window. Your own campaign windows layer on top and can be tighter than the law; they can never be looser.

Working out the recipient's clock from the number is done in three steps. Google's libphonenumber gives the country. For US and Canadian numbers that is not enough — the country spans six time zones — so a hand-maintained table of about 170 area codes maps each to a latitude and longitude, which a timezone-finder turns into an IANA zone; a 212 number becomes America/New_York. Daylight saving is then a library problem. If the contact record carries an explicit timezone, that wins over anything derived.

A schematic on black. A block labelled number leads to a block labelled country. From country, an upper lane labelled us / ca passes through blocks labelled area code table, coordinates and zone; a lower lane labelled other goes straight to zone. A block labelled contact timezone points into zone with a dashed arrow. From zone an arrow leads to a horizontal track labelled recipient day with a hatched span labelled window, the span filled in coral.
From a phone number to a window. Only North American numbers need the area-code table; everywhere else the country is enough. A timezone set on the contact overrides the derived one.

The policy is fail-closed on a confirmed violation and fail-open on uncertainty: a number the library cannot parse, or a zone that cannot be resolved, is allowed, because blocking on a guess would stop legitimate calls for a reason nobody could explain to the customer.

Hold, don't drop — and why direct calls are different

A campaign or scheduled call that arrives at the gate outside its recipient's window is not cancelled. It is deferred to the instant the window next opens, capped at seven days out, and the queue item is kept. An automated call is work the customer already set in motion; dropping it loses the lead. Before July the block orphaned the queue item, which looked like a dropped call from the campaign's point of view — the fix was to hold in place.

Direct calls took the opposite turn on 28 July 2026. A person pressing Call at 21:30 has made a decision, often with context the platform does not have — a callback the customer asked for, a different jurisdiction's rules, a business line. Refusing that call turned the floor into a ceiling. So a direct call outside the window now proceeds, and the violation is logged on the call for the audit trail. The eight such calls in the last 30 days are that log. The make-a-call docs state the division: automated calls are held, direct calls are your responsibility.

One more ceiling: ten automated calls a number a day

Added in August 2026, after a failure class that compliance lists do not catch: a retry counter that never incremented, so a contact that did not answer was redialled on every cycle. None of those numbers were on a do-not-call list and every attempt was inside calling hours; the list and the clock were both satisfied and the behaviour was still wrong. The queue now counts automated attempts per number per day and skips the contact at ten. Like the do-not-call check it fails open on a database error, and like calling hours it does not apply to a person pressing Call.

Read next
  1. Developers

    Turn detection that doesn't talk over you

    How our voice agent's turn detection decides a caller has finished, when an interruption is real and when to stay quiet, and the incidents behind it.

Questions about this piece? Write to us.