cleared-to-call · git:20260914.01b893f · 2026-09-14 · sha256 261d1b84375902ab
cleared-to-call git:20260914.01b893fA
Immutable. This exact content is served forever at /api/v1/blob/261d1b84375902ab.
---
name: cleared-to-call
description: Evaluate supplied account records against an experimental pre-call policy and flag literal opt-out phrases before integrating a CALL-E collection or account-servicing workflow. Offline advisory helpers only; the host owns calling, suppression persistence, and audit records.
license: MIT
---
# Cleared to Call
Use this skill when an agent is about to place an outbound call to a consumer
about money: a past-due balance, a payment reminder, a collection follow-up, or
any account-servicing call where the recipient did not initiate the contact.
`cleared-to-call` supplies offline advisory helpers, not a dialer or a legal
compliance certification. They read supplied records, evaluate a declared
policy, and print results. They do not place or end calls, write suppression
entries, or create or verify an audit chain. The integration requirements below
describe work a host must implement; they are not shipped runtime features.
An `ALLOW` result is not proof that a call is lawful or authorized.
The rules are data, not prose. They live in `assets/policy.json` and are
explained in `references/policy.md`.
## When To Use
Use this skill for:
- outbound collection, payment-reminder, or past-due account calls
- any batch of consumer accounts where each row must be checked before dialing
- workflows that must honor "stop calling me" during the call, not after it
- workflows collecting evidence for a human compliance assessment
- workflows explaining why the declared policy blocked a record
## When Not To Use
Do not use this skill to:
- place calls outside the United States, or under any rule set other than US
federal TCPA, FDCPA, and Regulation F - the policy is single-jurisdiction and
a different region needs a different policy file, not a workaround
- decide anything for inbound calls the consumer initiated
- override, soften, or "just this once" bypass a block reason
- guess a missing timezone, consent record, or phone number
- give the recipient financial, legal, credit, or settlement advice
- negotiate, threaten, or discuss consequences of non-payment
If the caller wants a block overridden, the answer is to fix the underlying
record, not the gate. A missing timezone is fixed by getting the timezone, not
by assuming one.
## Required Inputs
Each account must carry all of these. None of them may be inferred:
- `account_id`
- `display_name`
- `phone_e164` - E.164, for example `+15550101234`
- `timezone` - IANA zone name, for example `America/New_York`
- `amount_due` and `currency`
- `consent_on_file` - true or false
- `consent_timestamp` - required when `consent_on_file` is true
Check a batch before evaluating any of it:
```bash
node scripts/validate-input.mjs --file accounts.json
```
A row that fails validation is reported and skipped. It is never dialed.
Never derive `timezone` from the phone number, the area code, the operator's
locale, the server clock, or the country code. Rule 1 is a statement about the
recipient's local time, so a guessed timezone is a guessed legal position. If
the timezone is missing or unknown, the account is blocked.
## Try It
The skill ships a fictional batch that hits every branch. Nothing dials:
```bash
node scripts/validate-input.mjs --file assets/example-accounts.json
node scripts/evaluate-account.mjs \
--file assets/example-accounts.json --account-id A-1002 \
--suppression assets/example-suppression.jsonl --now 2026-08-28T13:30:00Z
node scripts/check-revocation.mjs --utterance "stop calling me"
```
At that instant `A-1001` is clear, `A-1002` is outside its local call window,
`A-1003` has no consent, and `A-1004` is suppressed.
## The Pre-Dial Gate
The host must run these four checks before dialing. Passing them is necessary
for this sample policy, not sufficient authorization for a real call.
| Rule | Requirement | Block reason |
| --- | --- | --- |
| R1 `call_window` | Local time at the recipient is inside 08:00-21:00 | `OUTSIDE_CALL_WINDOW` |
| R2 `consent_on_file` | Prior express consent is recorded, with a timestamp | `NO_CONSENT` |
| R3 `not_suppressed` | The number is not on the opt-out or DNC list | `ON_SUPPRESSION_LIST` |
| R4 `disclosure_ready` | The rendered script carries every required disclosure element | `MISSING_DISCLOSURE` |
Run the gate on one account:
```bash
node scripts/evaluate-account.mjs \
--file accounts.json \
--account-id A-1001 \
--suppression suppression.jsonl
```
Exit code `0` is ALLOW, `2` is BLOCK, `1` is unusable input. The printed JSON
holds every rule result, not only the failing one, so an audit reader can see
the whole evaluation.
When several rules fail, the reported `block_reason` is the first failure in
policy order. All four are still evaluated and printed for the host to retain.
Read `references/policy.md` for what each rule means, which authority it comes
from, and the property it is meant to enforce.
## The In-Call Revocation Handler
Rule 5 cannot be decided before dialing. A host integration has two duties;
only the literal transcript checker is included here:
1. The call task tells the agent to stop the moment the recipient revokes:
acknowledge once, end the call, do not ask why, do not offer alternatives.
2. After the call, run the transcript checker, then have the host persist any
required suppression entry and record the outcome.
The checker prints a required action; it does not carry it out. If the agent
talks past an opt-out, the host must still persist suppression and record
`opt_out`. A post-call check cannot retrospectively stop a running call.
```bash
node scripts/check-revocation.mjs --utterance "stop calling me"
node scripts/check-revocation.mjs --transcript transcript.json
```
Exit code `3` means the number must be suppressed. Only recipient turns count:
the agent saying "if you would like us to stop calling" is a required
disclosure, not an opt-out.
Once the host persists suppression and supplies the updated list, R3 blocks the
number on later evaluations. The host must retain the opt-out unless the
consumer asks for it to be reversed.
## How CALL-E Is Invoked
This skill does not place calls. Once an account is cleared, hand it to the
existing CALL-E route - the CALL-E CLI, MCP `plan_call` / `run_call` /
`get_call_run`, or whatever route the current client already uses:
```text
gate -> ALLOW -> plan_call -> inspect plan -> run_call -> get_call_run -> transcript -> revocation re-check
```
Requirements on the call task handed to CALL-E:
- the opening disclosure must be included verbatim and must be spoken first
- the task must instruct the agent to end the call on any revocation
- the task must restrict the agent to payment logistics
- the task must ask for one outcome from `promise_to_pay`, `dispute`,
`refusal`, `no_answer`, `opt_out`
- the destination must be exactly the cleared account's number
Inspect the plan before running it. If the plan targets a different number than
the cleared account, do not run it. Providers often echo the destination masked,
for example `...9724`: compare its last four digits with the cleared number. A
plan that names no destination at all cannot be checked, and an unchecked plan
is not a verified one - do not run it unless the operator explicitly accepts
that. Match symbol masks only; the call script itself says "account ending
1001", and reading that as a destination would refuse every call.
## Structured Result
Suggested host outcome shape, not an output implemented by the bundled helpers:
```json
{
"account_id": "A-1001",
"call_placed": true,
"block_reason": null,
"disclosure_given": true,
"outcome": "promise_to_pay",
"promise_date": "2026-09-05",
"opt_out": false,
"audit_ref": "aud_0af29e7f"
}
```
`outcome` is one of `promise_to_pay`, `dispute`, `refusal`, `no_answer`,
`opt_out`, `not_called`. A blocked account is always `not_called` with
`call_placed: false` and a non-null `block_reason`.
The host must derive `disclosure_given` from what was actually said, not merely
from the script. This package does not implement that post-call verification.
## Audit Record
Optional host design: retain decisions in a hash-linked JSONL log. This package
does not append entries or verify a chain. A hash chain alone is not proof of
legal compliance or protection against rewriting the entire log.
Such an entry can hold the timestamp, the account id, the masked phone number, the
decision, the block reason, every rule result, the outcome, the policy id and
version, and `prev_hash` plus `hash`.
The host must mask phone numbers in audit output, for example `+1******1234`.
## Dry Run
All bundled helpers are offline: they read input and print advisory results.
There is no live adapter, simulated dialer, suppression writer, or audit-chain
writer. Host integrations must provide and validate those features separately.
Never place a real call to test the helpers. Offline checks exercise the
declared sample policy, not the legality of a real calling program.
Place a real call only when the operator has explicitly asked for one, on a
number they are authorized to call.
## Safety Rules
Read `references/safety.md` for the full contract. Always:
- treat a phone call as a real-world side effect with legal consequences
- refuse rather than guess when a required field is missing or ambiguous
- mask phone numbers in every summary, log, and user-facing message
- never expose API keys, tokens, callback URLs, or confirmation tokens
- keep the call to payment logistics only, never advice or pressure
- honor an opt-out immediately and permanently
- never place a call for an account the gate blocked
- never edit or rewrite an audit entry
## Output Format
After a host-managed batch, report only actions the host actually performed:
- how many accounts were cleared, blocked, and opted out
- each blocked account with its block reason
- each cleared account with its outcome and masked number
- any opt-out, distinguishing suppression required from confirmed persistence
- any implemented audit verification result, or state that it is not implemented
If the gate blocked everything, say so plainly and give the reasons. A run that
places no calls because no account was clearable is a successful run, not a
failure.
See `references/examples.md` for worked batches, including the refusal cases.