Disclosure: I run NxFlowAI, an automation agency serving US businesses remotely from Mumbai. Not legal advice; check consent rules with your own adviser.
In US small-business automations, texting consent tends to live in the texting tool and nowhere else. Then a second tool (a CRM sequence, a review-request app, a reminder service) texts someone who replied STOP last month. The fix is boring and effective: one consent ledger that every automation checks before sending. It is a pattern I use as an AI automation agency building US texting workflows.
The table
CREATE TABLE consent_events (
id BIGSERIAL PRIMARY KEY,
phone_e164 TEXT NOT NULL, -- +1XXXXXXXXXX
channel TEXT NOT NULL, -- sms, whatsapp, email
purpose TEXT NOT NULL, -- transactional, marketing
event TEXT NOT NULL, -- opt_in, opt_out
source TEXT NOT NULL, -- web_form, keyword_reply, staff_entry
evidence TEXT, -- form id, message id, staff note
occurred_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Append-only. Never update a row; add a new event.
The check
def may_send(phone, channel, purpose):
last = db.latest_event(phone, channel, purpose)
if last is None:
return purpose == "transactional" and has_active_request(phone)
return last.event == "opt_in"
Every automation calls may_send right before sending, not when the job is queued.
Capturing opt-outs
- Inbound texts matching the standard opt-out words (the FCC's list is stop, quit, end, revoke, opt out, cancel and unsubscribe; carriers and providers may recognise more) write an
opt_outevent for all purposes on that channel. Plain-language requests such as "please stop texting me" should be caught too, by a person if not by code. - Staff can record an opt-out by hand ("customer asked on the phone").
- Opt-ins come from forms with clear wording, and the form id goes in
evidence.
Why append-only
When someone asks "why did we text this person?", you can answer with dates and sources. That is worth more than a boolean flag.
Test cases
- Opt-out on Monday, reminder scheduled Sunday: blocked.
- Opt-in for reminders only, marketing job runs: blocked.
- Number formatted three ways: same person (normalize to E.164).
We design ledgers like this during a 72-hour audit before building any texting flow. For the non-technical overview of that audit, see our guide to auditing a business workflow before AI.
Top comments (0)