//Outreach
Peer-to-peer texting
Facts checked against official sources · September 9, 2026
Peer-to-peer texting is a person pressing send on one message to one voter. A volunteer opens their queue, reads the campaign’s opener, edits it if they want, and taps send. The voter’s reply lands back in that volunteer’s thread, and the volunteer answers it. There is no send-to-all button anywhere in the workflow, and that is deliberate: a human choosing to send each message is what separates a conversation from an automated blast.
In Culper, P2P lives under Outreach → P2P. A manager builds a texting campaign from a saved list, assigns texters, and watches the queue drain. Texters (volunteers, precinct chairs, or staff) work their own conversations from a browser workspace. The same server code behind that workspace also powers the P2P API written for the volunteer mobile app, so the queue, the thread, and the outcome buttons are the same thing on every device.
What it is
A P2P campaign in Culper has four parts: a title, a saved voter list, an opener script, and a texting window. The opener is the first message every texter starts from; it can carry a {{first_name}} merge field and it can be rewritten before sending. The texting window is a start and end hour that the campaign chooses (the default is 9am to 9pm), and the server narrows it further to the 8am to 9pm floor that federal rules require.
When you create the campaign, Culper turns the list into contacts. Only voters who are still on the current county roll and have a phone on file become contacts. Any number already on the global suppression list loads as opted out, so no texter can ever open it. The creation toast tells you exactly how many contacts loaded and how many were skipped as opted out, so the number you see is the number you can actually text.
Each contact belongs to exactly one texter. That ownership is the whole model: a reply is addressed to the person who wrote the opener, and the thread stays with them. A manager assigning three more volunteers an hour later does not scramble existing conversations; only contacts that have never been messaged are redistributed.
How it works, from list to conversation
Managers see the P2P page in its management view; everyone else lands straight in the texter workspace. The management view lists each campaign with a progress bar split into closed, in conversation, needs reply, and to send, plus the count of texters. Opening a campaign shows five tiles (To send, Need reply, In conversation, Closed, Opted out) and the 100 most recent conversations with their last message.
The texter workspace is a two-pane screen: your queue on the left, the open thread on the right. Queue filters are All, Unread, Waiting (contacts who replied and are waiting on you), and New (contacts you have not messaged yet). The queue is sorted so unread replies come first, then unanswered conversations, then fresh contacts; a texter never has to hunt for the person waiting on them.
- Create. Candidate or campaign manager role. Pick a title, a saved list, the opener, and the texting hours. The campaign starts as a draft.
- Assign texters. Pick any current member of the campaign (volunteer, precinct chair, staff, manager, candidate). Unassigned to-send contacts are split evenly across the people you pick. Re-running assign rebalances; it does not silently do nothing.
- Unassign. Removing a texter returns their untouched contacts to the pool. Threads they already started stay with them until those conversations close.
- Set status. Draft, active, paused, or completed. Sending only works while the campaign is active; the workspace only lists active campaigns.
- Send. Open a contact, tap Use campaign script or write your own, and press send. Messages are capped at 480 characters so a text never silently splits into extra segments.
- Disposition. Mark the contact Supporter, Undecided, Opposed, Wrong number, or Do not contact. Dispositioning is separate from replying, because a wrong number needs no message back.
The guards on every send
There is one outbound path for P2P in the codebase, and it refuses anything it cannot prove is allowed. Every check below runs on every single message, not just at list-build time, because a voter can move, opt out, or fall off the roll between the day a list was built and the evening a volunteer reaches them.
- The texting campaign must be active and its parent campaign must not be archived.
- The contact must be assigned to the person sending. Staff, managers, and the candidate can send to any contact on their own campaign; a volunteer cannot.
- The voter must still be on the current county roll. A contact whose voter was retired from the roll is refused with a plain-language reason.
- The number is re-checked against the global, campaign-agnostic suppression list at send time. If it was added since assignment, the contact flips to opted out and the send stops.
- The send must fall inside the recipient’s local texting window. Culper resolves the time zone from the voter’s coordinates, ZIP, and area code; if none resolve, the send is refused rather than guessed.
- The sending line must pass a live A2P 10DLC check against Twilio at that moment. A stored “approved” status is not trusted.
- Every message carries a client dedupe key, so a double-tap or a lost network response returns the original message instead of texting the voter twice.
- Provider failures are recorded on the message with the error, and the texter sees a retryable error rather than a silent nothing.
Replies, STOP, and delivery status
Inbound texts arrive on one signed Twilio webhook shared by every SMS product in Culper, so there is exactly one definition of STOP on the platform. A reply is matched by phone number to the contact’s active conversation, newest first, so a number that appears in two campaigns lands in the thread it is actually talking to. The contact’s unread count goes up, the thread moves to the top of the texter’s queue, and the texter gets a push notification with the first 140 characters of the reply.
STOP, UNSUBSCRIBE, CANCEL, END, and QUIT are honored immediately. The number is written to the global suppression list, consent records for it are revoked across every campaign, and the P2P contact is marked opted out. The conversation history is kept, so the texter can see that the person answered and what they said; nothing further can be sent to them from any campaign.
Twilio’s delivery callbacks update each message through queued, sent, delivered, undelivered, and failed, with the carrier error code when there is one. A texter can tell a delivered message from one the carrier dropped, which matters when deciding whether to follow up at the door instead.
Sender pool and attachments
A P2P campaign can send from an explicit pool of numbers. Each contact is pinned to one number from that pool for the life of the conversation, so a voter always sees the same sender; a thread that changes numbers halfway through reads as spam. The numbers share a messaging service with the campaign’s poll line, which means they inherit its approved A2P registration instead of waiting on a second review.
A campaign can also carry one attachment: a JPG, PNG, GIF, or PDF under 4 MB, set by staff or a manager on the P2P page. Texters cannot upload their own media; they get an Attach checkbox in the composer, and the file that goes out is the one the campaign chose. Messages with an attachment send as MMS, which costs more per message than SMS, and the thread records exactly which file each message carried even if the campaign swaps the flyer later.
How a local campaign uses it
School board, persuasion window. You have a saved list of 2,400 likely voters in the district with a mobile number on file, and six volunteers who can each give two evenings. Assigning all six splits the list into 400 contacts each. If a volunteer opens roughly 80 conversations an hour, the whole list is opened in about five volunteer-hours per person, and the replies (mostly “who are you” and “what do you stand for on the bond”) are answered by the same person over the following days. Supporters and undecideds get dispositioned, and those tags follow the voter into your mail and walk lists.
City council, election eve. Your door program identified 900 supporters over the summer, and you save them as a list. Three staff members text a reminder the night before with the polling location and hours. The most common reply is a polling-place question, which a human answers in seconds. Anyone who replies that they already voted gets closed out so nobody wastes a knock on them.
County party, primary runoff. A county party loads a 15,000-contact list and assigns twenty precinct chairs. The campaign attachment is a one-page PDF sample ballot. Chairs work their own precincts from home, do-not-contact marks flow to the global suppression list so no candidate on the slate texts those people again, and the party sees the whole funnel on one screen.
Why it matters
The legal distinction between peer-to-peer texting and an automated blast rests on a person choosing to send each message. Culper builds that distinction into the code rather than the training: there is no batch send function, the sender is always the signed-in human, and the send is refused unless the contact is theirs. The rules, the FCC’s 2020 ruling on P2P, and the registration you still need are laid out in Political texting rules: TCPA and 10DLC.
Texting is also the cheapest way to turn a win number into contacts. A council seat that needs 2,000 votes is a few thousand conversations, not a media buy; the arithmetic is in How many votes does it take to win a local election. In vote-by-mail states, the same queue is the fastest way to chase non-returners in the final week, which Ballot chase in vote-by-mail states walks through.
What it does not do
P2P is not a broadcast tool, and it is not meant to become one. If you want to send one message to a whole list at once, use SMS outreach, which has its own consent gating and reporting.
- No scheduled or automated sends. Every message is sent by hand, during the recipient’s texting window, by the person it is assigned to.
- No per-texter media. One attachment per campaign, chosen by staff.
- No automatic replies beyond the carrier’s STOP confirmation. Texters answer in the thread.
- Conversations do not auto-reassign. If a texter goes quiet, a manager closes the threads or waits for them to finish before rebalancing.
- Messages are capped at 480 characters, and the opener always carries opt-out language.
- Sending requires a messaging service with a verified A2P 10DLC political campaign, checked live. Registration is done once, in SMS settings, and can take days to weeks.
- Documented consent is part of the design. Culper records consent from its opt-in forms and can enforce a per-recipient consent check at send time; the consent posture for a given list is a decision your campaign makes with counsel, not something the software makes for you.
Works with
P2P starts from a saved list and ends in dispositions on the voter record, so it depends on the pieces on either side of it.
- SMS outreach for one-to-many sends, A2P registration, and the shared STOP webhook.
- Search, lists, and tags to build the audience and to use dispositions later.
- Volunteer coordination to get texters onto the campaign before you assign them.
- Volunteer mobile app, whose P2P API is the same code the web workspace uses.
- Compliance and data security for the suppression list and per-campaign isolation.
- Block walking, the other side of the same supporter-ID loop.
Frequently asked questions
Who can send P2P texts in Culper?
Any member of the campaign who has been assigned contacts: volunteers, precinct chairs, staff, managers, or the candidate. Staff and above can also send to any contact on their own campaign without an assignment. Creating campaigns, assigning texters, and changing status require the candidate or campaign manager role.
What happens when a voter replies STOP?
The number goes onto a global, campaign-agnostic suppression list the moment the STOP arrives, consent records for it are revoked everywhere, and the P2P contact is marked opted out. The texter sees the reply in the thread but cannot send anything further. No campaign on the platform can override that list.
Can I send the same message to everyone on the list at once?
No. P2P has no bulk send by design; a person presses send on each message. For a one-to-many send to a saved list, use SMS outreach, which runs through its own registration, suppression, and reporting pipeline.
Does Culper need a special phone number for P2P?
P2P sends through a Twilio messaging service with a verified A2P 10DLC political campaign registration, checked live before every send. A campaign can define a pool of numbers so each voter always hears from the same one, and P2P can share the registered service used for polls so it does not wait on a second review.
What are the texting hours?
A campaign chooses its own window, but the server clamps it to 8am to 9pm in the recipient’s local time, resolved from the voter’s coordinates, ZIP, and area code. If a contact’s time zone cannot be resolved, the send is refused rather than guessed. Outside the window the texter sees a retryable message and can try again later.
Can texters attach a photo or flyer?
Yes, one per campaign. Staff or a manager uploads a JPG, PNG, GIF, or PDF under 4 MB, and every texter gets an attach checkbox in the composer. Texters cannot choose their own media. Attached messages go out as MMS, which costs more per message than plain SMS.
Put your volunteers on the phones they already carry
Keep reading