The 26-rule check every message and call has to pass, what we do not have yet, and how client data is kept apart.">
Home Services UGC Factory Process Pricing About Contact
Security & compliance

The gate every message has to pass.

Updated 21 September 2026Rules version 1.0.0Rules updated 27 August 2026

Most outbound tools describe compliance as a feature. Here it is a gate that every message passes through, it fails closed, and it is the same gate in every engine we run. This page documents how it actually works, rule by rule, including the parts that are unflattering.

26rules in the dispatch contract
50conformance fixtures run in CI
4of the 26 rules can return pass
blockthe default verdict when nothing matches

The dispatch gate

Every outbound email, SMS, and call — human-dialled or automated — is evaluated before it is queued. The decision is made by a single shared contract, compliance/rules.json, which both of our engines load: compliance_gate() for cold prospecting and dispatch_gate() for consented inbound leads.

Two properties make the gate testable rather than decorative:

  • Evaluation is pure. No I/O, no database calls, and no clock reads happen inside the gate. Every input is resolved into a flat facts object first — phone type, timezone, consent evidence, suppression scope, DNC list age — and only then are rules applied. This is what makes a fixture suite meaningful.
  • It fails closed. Rules are evaluated top to bottom and the first complete match decides. If no rule matches at all, the verdict is block with reason NO_RULE_MATCHED. An unrecognised state never results in a message reaching a consumer.

Three verdicts, and why defer exists

VerdictMeaning
passDispatch may proceed now.
blockDispatch must not happen. Terminal for this attempt. An events row is written with the reason code.
deferDispatch is lawful but not yet permitted. The message is rescheduled to the next open window and re-evaluated then.

The third verdict is the one that matters operationally. A message that arrives outside a lead's calling window is not dropped and is not sent anyway — it is rescheduled. Silently discarding it would look identical to a delivery failure in the logs, and we would never find out which one happened.

The 26 rules

Twenty-two of the twenty-six rules exist to stop a message. Four allow one.

RuleVerdictReason code
R01blockDISPATCH_DISABLED
R02blockTENANT_MISMATCH
R03blockCLIENT_NOT_ACTIVE
R04blockLEAD_MARKED_INVALID
R05blockSUPPRESSED_ALL_CHANNELS
R06blockSUPPRESSED_CHANNEL
R07blockCONSENT_REVOKED
R08blockCONSENT_ABSENT
R09blockCONSENT_TYPE_UNACCEPTED
R10blockCONSENT_EVIDENCE_INCOMPLETE
R11blockCONSENT_DISCLAIMER_DRIFT
R12blockTCR_NOT_APPROVED
R13blockPHONE_TYPE_UNKNOWN
R14blockPHONE_INVALID
R15blockAI_VOICE_DISABLED
R16blockAI_VOICE_NOT_DISCLOSED
R17blockDNC_LIST_STALE
R18blockDNC_MATCH_NO_CONSENT
R19blockDUPLICATE_DISPATCH
R20blockRATE_LIMIT_EXCEEDED
R21deferOUTSIDE_QUIET_HOURS_STATE
R22deferOUTSIDE_QUIET_HOURS
R90passPASS_SMS_CONSENTED
R91passPASS_VOICE_HUMAN_CONSENTED
R92passPASS_VOICE_AI_CONSENTED
R93passPASS_EMAIL_CONSENTED

Quiet hours, in the lead's timezone

Calling windows are computed from the lead's timezone, resolved from their ZIP or phone number — never from ours. Kelparo operates from IST, and using the operator's clock is the expensive version of this mistake.

ScopeWindowAuthority
Default08:00 – 21:00TCPA
Florida08:00 – 20:00Florida Telephone Solicitation Act
Oklahoma08:00 – 20:00Oklahoma Telephone Solicitation Act
Washington08:00 – 20:00Washington CEMA / mini-TCPA

Quiet hours apply to SMS, human voice, and AI voice. They do not apply to email, which is governed by CAN-SPAM and carries no time-of-day restriction. We say so rather than claiming a protection we do not implement.

For the consented-inbound engine, express written consent is the only accepted basis. A Meta lead form carrying a compliant custom disclaimer creates it; nothing else qualifies. Five evidence fields must all be present or the message is blocked under CONSENT_EVIDENCE_INCOMPLETE:

  • disclaimer_text — what the person actually agreed to
  • disclaimer_hash — checked against the live form on every dispatch
  • meta_lead_id, form_id, submitted_at

If the disclaimer on the live form no longer matches the hash we stored, dispatch is blocked under CONSENT_DISCLAIMER_DRIFT. Someone editing a lead form should not be able to retroactively change what past leads consented to.

Consent evidence is retained for five years. Revocation is honoured immediately, against a legal floor of ten business days, and is detected in free text rather than only on the literal keyword. “Stop texting me”, “take me off your list” and “not interested, don't contact me” all revoke.

DNC scrubbing

Our copy of the National Do Not Call registry may be at most 31 days old. Past that, the gate blocks under DNC_LIST_STALE rather than scrubbing against stale data and reporting a clean result.

A valid express written consent generally permits contact despite a DNC listing. We still run the scrub and we still write the override to the event log, because the log is the defence.

AI voice disclosure

An AI voice agent cannot dial unless two separate conditions hold: the client has AI voice enabled, and the consent on file explicitly disclosed automated calls. Failing either produces AI_VOICE_DISABLED or AI_VOICE_NOT_DISCLOSED. Phone type is resolved before any call is placed; an unknown or invalid number is blocked, not attempted.

Duplicate dispatch is blocked inside a 1,440-minute window. Meta redelivers leadgen webhooks, and a double-text is both a consumer complaint and a 10DLC campaign risk.

Keeping two gates honest

Our engines are separate repositories with separate deploy cycles, so the real risk is not a wrong rule — it is two copies of a right rule drifting apart. The mechanism:

  1. compliance/rules.json and compliance/fixtures.json are canonical. They are edited in one place.
  2. A sync script copies them into each consumer repo and writes a .compliance-lock containing the SHA-256 of each file.
  3. Every consumer's CI recomputes those hashes and fails the build if its vendored copy does not match the lock.
  4. All 50 fixtures must pass in every engine, asserting both the verdict and the reason code — not just that something was blocked, but that it was blocked for the stated reason.

A rule set that can be stale at runtime is worse than one that is obviously stale at build time. Drift is a red CI run instead of a fine.

The two gates deliberately default in opposite directions: cold outbound blocks a mobile number without express consent, while consented inbound passes it. That difference is a documented rule with fixtures on both sides, not an accident of two copies diverging.

Tenant isolation

Every data-layer function takes an organisation ID as its first argument and every query filters on it. Row-level security is applied in the database in addition to the application filter. Isolation is covered by dedicated test suites — tenancy, retrieval isolation, SSRF, and lockdown — rather than by convention.

Kill switches

Two environment flags, both defaulting to false, gate all outbound: SEND_ENABLED for our own prospecting and DISPATCH_ENABLED for messages to a client's leads. With the flag off, messages are rendered and queued but never transmitted, and the dashboard labels the mode DRY-RUN SIMULATION rather than showing numbers that imply delivery.

A failed transmission never looks like a send. The sequence step does not advance, the row is not marked sent, and a temporary failure is rescheduled rather than discarded — otherwise the database grows a send history that never happened.

What we do not have

The useful version of a page like this includes the gaps.

  • We are not SOC 2 certified. No audit is in progress.
  • The compliance configuration has not yet been reviewed by a US attorney. It is researched, and it is carefully tested against its own fixtures, but tests prove internal consistency, not legal correctness. That review is a precondition for switching DISPATCH_ENABLED to true for any client.
  • The gate is enforced in application code and tested in CI, not enforced by a database constraint that application code could not bypass. Moving the check into a constraint is a stronger guarantee than the one we ship today.
  • US only. The rule set encodes US federal and state telemarketing law. It has no GDPR lawful-basis model and no EU equivalent, so we do not run outbound into the EU or UK.
  • Our engines have not yet run at volume. Everything above is built and tested; little of it has been operated against production traffic. We would rather tell you that now than have you discover it in month two.

Before this page is treated as binding. Kelparo's compliance configuration carries its own disclaimer in compliance/rules.json: “Researched, not legal advice. A US attorney must review this configuration before DISPATCH_ENABLED is set true for any client.” The same applies to this document. Items marked TODO are facts we have not settled yet, and we would rather show you the gap than fill it with something invented.