- Sales
- Prospecting
- Public records
- Lead generation
- ICP
- Outbound
US Public Records Prospector
Find US businesses that fit your ideal customer in free public records (NPI healthcare registry, SEC EDGAR, federal contract awards, city business licenses), filter them against your ICP, find each business's website and decision-maker, and hand them to the sales-playbooks skill to write and send "we do X for businesses like yours, interested?" by email or LinkedIn after you approve each one. Use on "find me clients in <city>", "prospect <industry> in <state>", "build a lead list from public records", "new businesses in <city>".
SKILL.md
US Public Records Prospector
Public records say who exists, where, since when and often who runs it. This skill turns that into a qualified, approved outreach pipeline. It owns the first two steps (records and ICP filter) and orchestrates other skills for the rest:
| Step | Who does it |
|---|---|
1. ICP interview, saved to sales/icp.json |
this skill |
| 2. Pull records, filter, score | this skill: scripts/records.py |
| 3. Find the website and the decision-maker | agent-reach (web search, read pages) + sales-playbooks (prospecting_tactics.md, account_navigation.md) |
| 4. Load into the pipeline, record signals | this skill: records.py load → sales-playbooks pipeline.py |
| 5. Qualify and write the first touch | sales-playbooks (lead_qualification.md, signal_playbook.md, email_frameworks.md), anti-ai-slop-writing |
| 6. Owner approves, gate checks, send | sales-playbooks (gate.py, pipeline.py approve) → google-workspace or outlook (email), linkedin (Linkedroid) |
| 7. Replies, opt-outs, follow-ups | sales-playbooks (reply_handling.md, objection_handling.md) |
If a skill in that table is not installed, say which one and stop at the step before it. Never replace a missing send skill with something improvised.
Read references/us-contact-rules.md once before the first send. The short version: email or
LinkedIn first, owner approves every message, phone numbers are for the owner's own calls, never a
first-touch text.
1. The ICP (once, then reuse)
If sales/icp.json does not exist, ask in one message:
- What do you sell, and what result does it get the customer? (one sentence)
- Who buys it: industry, and the type of business inside it (e.g. "independent dental practices, not chains")
- Where: states, cities or ZIP prefixes
- Any "why now" that matters: new business, recently licensed, won a federal contract, public company
- Who to leave out: big chains, competitors, existing customers
- How many prospects a week you want to look at
Copy templates/icp.example.json to sales/icp.json and fill it from the answers. Pick the
sources from references/us-sources.md that hold that kind of business and say which you chose and
why. Also make sure sales/config.yaml and sales/product_knowledge.md exist (the sales-playbooks
onboarding writes them); offer and product claims only ever come from there.
2. Pull, filter, score
python3 scripts/records.py presets # built-in city datasets
python3 scripts/records.py fetch npi --taxonomy "dentist" --state TX --city Austin --max 200
python3 scripts/records.py fetch socrata --preset chicago-licenses --since 2026-08-01 --where "application_type='ISSUE'"
python3 scripts/records.py filter sales/records/*.jsonl --icp sales/icp.json
filter writes sales/records/shortlist.jsonl, best score first, and reports what it dropped and
why. Show the owner the top 10 (name, city, why it scored, source link) before going further, and
adjust icp.json if they say the list is off. SEC needs SEC_USER_AGENT="Name email@domain" set.
3. Website and decision-maker
python3 scripts/records.py needs-website sales/records/shortlist.jsonl --max 25
For each row, search the query with agent-reach, open the result that matches the name and the city or phone, and confirm it on the site's contact or about page. Then:
python3 scripts/records.py set-website sales/records/shortlist.jsonl <source_id> https://their-site.com
No confident match: leave it; it stays out of the pipeline. NPI records already name the authorized
official (often the owner); for the others, find the owner or decision-maker on the team/about page
or through Linkedroid, following account_navigation.md. Find the email the way
prospecting_tactics.md describes and record its status honestly (verified, risky, guess).
4. Load
python3 scripts/records.py load sales/records/shortlist.jsonl # --dry-run first if unsure
Each business becomes a company (deduplicated by domain) with these signals: PUBLIC_RECORD (source
and link), NEW_BUSINESS (when the record date is recent) and BUSINESS_PHONE. The named person
becomes a contact. Re-running only loads new rows. Then add what you found in step 3 with
pipeline.py add-contact … --email … --email-status … and pipeline.py observe (e.g. NO_WEBSITE,
NO_ONLINE_BOOKING, HIRING_ROLE), and score each contact with pipeline.py score.
5-7. Write, approve, send, follow up
Hand over to sales-playbooks and follow it exactly. The first email is the "we do X for businesses like yours, interested?" message, made specific by one real signal from the record or the site:
Subject: booking at Lakeview Family Dental
Dr. Patel, I was looking at Austin practices that opened online booking this year and noticed Lakeview Family Dental still takes appointments by phone only.
We build booking-ready sites for independent dental practices, so patients can book after hours. Would that be useful for you?
[owner's name, company, postal address] · Reply "stop" and I won't write again.
Every message goes into pipeline.py queue, is shown to the owner, and is sent only after their yes
and a passing gate.py. Opt-outs go to pipeline.py suppress at once.
Phone numbers
BUSINESS_PHONE is for the owner's own calls. When they want a call list:
python3 ../sales-playbooks/scripts/pipeline.py cohort --must BUSINESS_PHONE,PUBLIC_RECORD
Give them the list with the business, the person, the reason to call and the number. Never text or
auto-dial these numbers, never send a first-touch SMS or WhatsApp, and never look up personal cell
numbers. Texting starts only after the person texted first or opted in (see references/us-contact-rules.md).
Weekly loop
When the owner wants this on a schedule: fetch the sources in sales/icp.json with --since set to
the last run, filter, find websites for the new rows, load, and report how many new prospects are
waiting for approval. Never send on a schedule without the owner's approval of each message.
Rules
- Only the sources in
references/us-sources.mdor official APIs and open-data portals. No scraping sites whose terms forbid it, no people-search sites, no bought consumer lists. - Every claim in a message traces to the record link, the business's own site, or
product_knowledge.md. - Owner approval and the gate on every send; opt-outs are permanent.
- Business contact data only. No home addresses, personal cell numbers or private details.
- Say you are automated if anyone asks.
Files
4 files ship with this skill. They are installed beside the SKILL.md.
# US contact rules for this skill
Practical rules the agent follows, not legal advice. When the owner's situation is unusual
(healthcare, finance, a regulated product, a state with its own telemarketing law), tell them to
check with their lawyer before the first send.
## Email: the default first touch (CAN-SPAM)
Cold B2B email is legal in the US if it follows CAN-SPAM, and the FTC says the law has no
business-to-business exception. Every message:
1. Accurate From, To and Reply-To: the owner's real name and domain.
2. A subject line that matches the content. No fake "Re:" or "Fwd:".
3. It is clear the message is a commercial offer (a plain, honest pitch does this).
4. The owner's valid physical postal address in the signature.
5. A clear, working way to opt out ("Reply 'stop' and I won't email again" is enough for 1:1 mail).
6. Opt-outs honored within 10 business days, and in practice immediately: `pipeline.py suppress`.
Penalties run per email, so the sales-playbooks gate runs on every message. The `sales-playbooks`
skill already enforces the signature, one link at most, no tracking pixels and suppression.
## LinkedIn: the second channel
Through Linkedroid only (the `linkedin` skill), inside its daily limits. Never browser automation.
## Phone numbers: for the owner's own calls only
The records often include a business's main line. The agent stores it as `BUSINESS_PHONE` and puts it
in a call list for the owner. It does **not** text or call anyone.
- **Texts count as calls under the TCPA.** Marketing texts or calls sent with an autodialer or a
prerecorded or artificial voice need the person's prior express written consent. Damages run $500
to $1,500 per text, and class actions are common.
- **Do Not Call.** Many courts have applied the National Do Not Call Registry to text messages (some
disagree since 2025, so treat texts as calls). A number on a public record can still be a cell phone
or a home business line on the registry.
- **Carriers block unregistered business texting.** Business SMS in the US needs A2P 10DLC brand and
campaign registration, and carriers require opt-in consent for marketing messages anyway.
- **States go further.** Several states (for example Florida, Oklahoma and Maryland) have their own
telemarketing laws covering automated texts and calls.
So texting is only for people who **texted the owner first or opted in** (a form, a reply that says
"text me"), from a registered number, with STOP honored. The skill never sends a first-touch text.
When the owner calls a business line themselves: business hours in the business's time zone, say who
they are and why in the first sentence, and if asked not to call again, add the number to the
suppression note and never call again.
## Data the agent may keep
Business names, business addresses, published business phones and emails, a named owner or officer
from the public record, and the link to each record. Not personal cell numbers from people-search
sites, not home addresses, not anything about a person's private life.