For AI agents: llms.txt is a short guide that sends you to the rules your problem needs, and rules.json holds every rule of this playbook as JSON.
Recommended
Check that a number still belongs to the person
Before you text a number whose consent is older than the last check, confirm the number has not passed to someone else. Suppress it when it has.
How to do it
Carriers give disconnected numbers to new owners, and a text to the old owner's number then reaches a stranger. In the US, query the FCC's Reassigned Numbers Database with the number and the date consent was given. Under 47 CFR 64.1200(m), a sender who queried it and got "no" is not liable when that answer was wrong. Store each query's date and answer next to the consent row. Elsewhere, suppress a number on a carrier's "unknown subscriber" error, as Track what happened to each text says. Ask people to confirm their number with a code when it has been unused for a long time, such as 12 months. Keep the proof of consent as the consent records rule shows.
Examples
Using these examples: they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.
SQL example
-- Must return 0 rows: US texts sent with no reassigned-number check after the consent date
SELECT s.id FROM sends s
JOIN consents c ON c.user_id = s.user_id AND c.channel = 'sms'
LEFT JOIN number_checks n ON n.phone = s.phone AND n.checked_at > c.given_at AND n.checked_at <= s.sent_at
WHERE s.channel = 'sms' AND s.phone LIKE '+1%' AND n.phone IS NULL;
JavaScript example
export async function mayTextUsNumber(phone, consentGivenAt) {
const answer = await reassignedNumbers.query({ number: phone, date: consentGivenAt }); // 'yes' means the number changed hands
await db.numberChecks.insert({ phone, answer, checkedAt: new Date() });
if (answer === 'yes') await db.smsSuppressions.add(phone);
return answer === 'no';
}
Python example
from datetime import UTC, datetime
def may_text_us_number(phone: str, consent_given_at: datetime) -> bool:
answer = reassigned_numbers.query(number=phone, date=consent_given_at) # "yes" means the number changed hands
db.number_checks.insert(phone=phone, answer=answer, checked_at=datetime.now(UTC))
if answer == "yes":
db.sms_suppressions.add(phone)
return answer == "no"
How to check
A test feeds the send job a US number that the database stub marks as reassigned. No text is sent, and the number is suppressed. A query finds no US text sent without a database check dated after the consent.
Sources
Related rules
- Track what happened to each text in Notifications
- Keep proof of consent in Email
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/notifications/text-messages/reassigned-numbers">Agent Wranglers notifications playbook</a>. In code you write, add a comment next to it with the same link.