Text-o-Matic

Prepare personalized texts from a list. Your contact and message data is processed locally in your browser and is not uploaded to Text-o-Matic.

Step 1

Your list

Paste phone numbers or tabular data, or upload a CSV or Excel file.

Paste

One phone number per line, or paste comma/tab-separated columns.

Upload CSV or Excel

CSV, .xls, and .xlsx files are read locally in your browser.

How it works

Welcome to Text-o-Matic

Text-o-Matic is basically a faster way to do the repetitive parts of one-to-one texting. Instead of copying and pasting the same message over and over, keeping track of who you've already contacted, and manually managing recipient details, you can load a list once and move through a queue of ready-to-send personalized messages.

Write your message once, add names or other details with merge fields, and Text-o-Matic prepares each message for you while helping you keep track of your place in the list.

  1. Add your list by pasting phone numbers or uploading a CSV or Excel workbook.
  2. Choose the phone column, recipient name, merge fields, contact-card fields, and any columns you want to ignore.
  3. Write a message using fields like {{First Name}}.
  4. Review missing data and customize fallback messages when needed.
  5. Work through the queue with SMS or WhatsApp on this device, or transfer the prepared list and messaging-app choice directly to another device.
Private by design — and you can verify it.

Your contacts, uploaded file contents, phone numbers, merge-field values, and message text are processed locally in your browser. Text-o-Matic does not upload that information to a Text-o-Matic server. Unfinished queues can be stored temporarily in this browser so they can be resumed.

Don't take my word for it: once Text-o-Matic says Offline ready, disconnect from the internet and keep using it; inspect the open-source code; or watch your browser's network traffic while you use the app.

Why is Text-o-Matic free?

Text-o-Matic is a labor of love and a bit of community service. I kept seeing grassroots groups and other small organizations run into the same problem: they needed a faster way to send personalized messages without adopting an expensive or complicated mass-texting platform. It seemed like an interesting problem to solve and one that could help a lot of people.

Its local-first design also makes it extremely cheap for me to run. There's no application server, account system, or database of contact and message data to pay for, and the site adds essentially no cost beyond infrastructure I already maintain for my own projects. That lets me offer it for free without relying on advertising, subscriptions, or selling user data.

Questions, issues, or comments? nick@nickspeelman.com

Version … Support Text-o-Matic

Privacy

How Text-o-Matic handles your data

Text-o-Matic does not upload your contact lists or message text. Uploaded file contents, phone numbers, names, merge fields, contact-card fields, and prepared messages are processed locally in your browser. Before you start a texting queue, the imported list and prepared messages are normally kept only in the app's working memory rather than saved to persistent browser storage. Text-o-Matic does not intentionally include that information in network requests made by the app.

No application can guarantee privacy or security with 100% certainty. I have designed Text-o-Matic to keep your data as private and secure as reasonably possible while it is inside the app, and I have chosen to be as transparent as possible about how it works and where the limits are. If you are working with especially sensitive information, Text-o-Matic is designed so you can reduce your exposure further.

You can verify the local-first design yourself. Once Text-o-Matic reports Offline ready, it can keep preparing lists and messages with your internet connection turned off. You can also inspect its network traffic while you work or read the source code on GitHub.

Site loading and infrastructure. Squarespace Domains provides DNS and GitHub Pages hosts Text-o-Matic, so those providers process the ordinary technical requests involved in locating and loading the site. While online, the app also loads two pinned libraries from their CDNs. On the production site, Text-o-Matic sends one empty request at page load to a small Cloudflare-hosted counter. The counter's Worker source, D1 schema, and deployment configuration are published alongside Text-o-Matic's source, and production deployments are built from the public production repository. The live Worker also exposes its deployed Git commit and Cloudflare Worker version through a read-only version endpoint and response headers. The application-managed database stores only one aggregate total per day; Text-o-Matic does not store an individual visit record, analytics identifier, IP address, browser information, or referrer. Cloudflare still processes the ordinary network information needed to receive the request. None of these requests intentionally includes your contact list, phone numbers, merge-field values, or message text. Once Text-o-Matic reports Offline ready, you can disconnect before importing sensitive data.

Device Transfer does not upload your list. Text-o-Matic places the transfer payload directly in one or more QR codes rather than storing a server-side copy. Transfer links use the URL fragment — the part after # — to carry transfer data. Browsers do not send that fragment to the web server when requesting the page, so the transfer contents are not sent to GitHub Pages as part of loading Text-o-Matic. See exactly how this works.

Device Transfers are encrypted by default. In encrypted mode, Text-o-Matic encrypts the prepared transfer locally using AES-GCM before creating the QR codes. The QR codes contain encrypted data. The separate six-word code needed to decrypt the transfer is shown only on the first device and is not included in the QR codes. It stays hidden on the QR screen until you choose to reveal it after scanning. Encryption can be turned off for an easier transfer; when it is off, the QR codes contain readable contact and message data.

Temporary Device Transfer data is cleaned up. During a multi-code transfer, received parts are stored temporarily in IndexedDB so additional QR codes can be added. Encrypted transfers remain encrypted in that temporary storage until the separate transfer code is entered. The temporary transfer record is removed when the transferred list becomes the active texting queue. Abandoned or incomplete transfers expire after 24 hours of inactivity and are removed the next time Text-o-Matic runs.

Contact cards are created locally too. When you choose to save a contact, Text-o-Matic builds a standard .vcf contact-card file from the mapped name, phone number, and other contact fields in your browser. Your browser or device may describe this as a “download,” but Text-o-Matic is creating the file from data already on your device — it is not downloading the contact from a server or uploading the contact information somewhere to create it. Once you open or save the card in your contacts app, that app's own privacy, storage, sync, and backup behavior applies.

An unfinished texting queue is stored temporarily so it can be resumed. When you start texting, Text-o-Matic saves the prepared recipients, messages, queue position, statuses, and any mapped contact-card fields to IndexedDB in this browser. When every recipient has been messaged or skipped, that resume copy is removed immediately. Choosing Discard & erase also removes it immediately. An unfinished queue expires after 7 days of inactivity and is removed the next time Text-o-Matic runs. If Text-o-Matic is never opened again, the app cannot wake itself up later just to delete browser storage.

Closing an unfinished queue does not erase it. When possible, Text-o-Matic uses a browser-provided leave-page warning to remind you that unfinished work will remain available for resume. Browsers, especially on mobile devices, do not reliably allow a web app to run cleanup code as it closes. Closing the tab therefore does not erase the queue. Use Discard & erase in the active queue when you want the local resume copy removed immediately.

Other local browser data may remain. Text-o-Matic does not use cookies and does not use cookie-based analytics, advertising, or tracking. It stores some non-list information locally in this browser, such as preferences, usage counters, and recent column-mapping choices. These records do not contain recipient rows or prepared message text and may remain until the browser's site data is cleared.

Your browser and device have their own privacy and storage behavior. Private/incognito modes, storage-management policies, or clearing site data may delete Text-o-Matic's local storage earlier than described above, which can make an unfinished queue impossible to resume. Depending on your browser, operating system, backup settings, sync features, cloud backup, session or crash recovery, or device-management configuration, locally stored browser data or a briefly opened transfer address may also be handled by features outside Text-o-Matic's control. On a shared device or browser profile, another person with access to that profile may be able to reopen an unfinished queue.

Data outside Text-o-Matic is outside its control. A camera or QR-scanning app may keep its own scan history depending on its settings. With the default encrypted transfer, that history should contain encrypted transfer data rather than readable contacts or messages; with encryption turned off, it may contain the readable QR payload. Once a message is opened in your SMS app or WhatsApp, it is subject to that app's, service's, and where applicable your carrier's privacy, security, and retention practices. Text-o-Matic cannot view or delete information stored by those other apps or services.

Questions about privacy can be sent to nick@nickspeelman.com.

Version …

Maximum privacy

Reduce the opportunities for data to leave your device

No application can guarantee privacy or security with 100% certainty. Text-o-Matic is designed to keep contact and message data local, and you can use it in a way that reduces the number of other systems that have an opportunity to see that data.

  1. Consider installing Text-o-Matic as an app. Installing the Progressive Web App (PWA) can make offline use more convenient and reliable because you can launch Text-o-Matic directly without first navigating to the website. Installation itself does not provide additional encryption or guarantee greater privacy; the app still uses your device's browser technology and local storage. Learn how to install a PWA.
  2. Prepare every device while it is still online. Open Text-o-Matic on the device or devices you plan to use and wait until the footer says Offline ready. If you will use Device Transfer, do this on the receiving device too.
  3. Disconnect before importing sensitive data. Turn off Wi-Fi and mobile data (or otherwise disconnect the computer from the network) before you paste or upload your list or write messages. Text-o-Matic can continue preparing the list and queue from its cached files.
  4. Keep Device Transfer encryption on. For the strongest offline transfer, make sure both devices are already Offline ready, disconnect them from the internet, leave Encrypt transfer enabled, and then scan the QR codes. Keep the separate six-word transfer code away from the QR codes until the receiving device asks for it.
  5. Choose how you want to send. There can be a trade-off between keeping the device disconnected from the internet and protecting the message while it travels to the recipient. Ordinary SMS/MMS is not end-to-end encrypted. If your phone and carrier allow it, leaving Wi-Fi and mobile data off while sending ordinary SMS reduces opportunities for internet-connected software to communicate while Text-o-Matic is in use, but it does not make the SMS itself private from your carrier or the cellular messaging system. By contrast, iMessage is end-to-end encrypted, and some RCS conversations are as well, but they require an internet connection. RCS encryption depends on the devices, apps, carriers, and conversation involved, so do not assume an RCS message is encrypted unless your messaging app indicates that it is. One option is to prepare everything in Text-o-Matic while offline, then reconnect only when you are ready to send through an end-to-end-encrypted service.
  6. Remember what Text-o-Matic can control. Preparing while offline reduces opportunities for browser or internet-connected software to transmit information while you work. The protection of the message after Text-o-Matic hands it to your messaging app depends on the sending method you choose. Text-o-Matic cannot control your messaging app, carrier, messaging service, the recipient's device, or their backup and synchronization settings.
  7. WhatsApp and other internet-based messaging services require a connection. You can prepare the queue while offline, but you must reconnect to send through an internet service. If that service provides end-to-end encryption, that may protect the message in transit more strongly than ordinary SMS, but the service and its apps still operate outside Text-o-Matic's control.
  8. Erase the Text-o-Matic session when you are done. Finish the queue or choose Discard & erase for an unfinished queue. For especially sensitive work, also review browser/device features such as history sync, cloud backup, clipboard history, keyboard synchronization, crash reporting, and session recovery. Clearing Text-o-Matic's browser site data provides a stronger local reset, but it also removes the offline cache and preferences, so you would need to prepare it for offline use again next time.
Practical goal: reduce the number of systems that can observe your data rather than promise an impossible form of perfect privacy.

Version …

Verify it yourself

Inspect Text-o-Matic's network traffic

You can use your browser's built-in developer tools to see every network request Text-o-Matic makes while you use it.

  1. Open Text-o-Matic, then open your browser's developer tools. In Chrome, Edge, and Firefox, F12 or Ctrl+Shift+I usually opens them.
  2. Select the Network tab and reload the page so you can see the requests made when the app starts.
  3. Paste or upload a test list, map its columns, and build a message while the Network tab remains open.
  4. You will see ordinary requests needed to load the site and its pinned libraries while online. On the production site, you will also see one empty POST request to text-o-matic-pageviews.nick-958.workers.dev/view; on the dev site, the equivalent request goes to the separate dev Worker and does not write to the production database. Select that request and inspect its response headers: X-Text-O-Matic-Commit identifies the public Git commit used to tag the deployed Worker version, and X-Text-O-Matic-Worker-Version identifies Cloudflare's Worker version. What you should not see is Text-o-Matic transmitting your uploaded file contents, contact list, phone numbers, merge-field values, or message text.
  5. For an additional check, wait until the footer says Offline ready, disconnect from the internet, and continue using the app.
One important boundary: when you choose to open a prepared message, Text-o-Matic hands the recipient and message to the messaging app or service you selected. Network activity from that app or service is outside Text-o-Matic.

You can also inspect the app source, the pageview Worker source, the published D1 schema, or the live Worker version metadata.

Version …

Device Transfer

How can a QR code work without uploading my list?

A QR code is just machine-readable data, and it can store a surprisingly large amount of it. It does not inherently mean “internet” or “website.” Text-o-Matic puts your prepared contact and message data directly into one or more QR codes instead of uploading it somewhere and creating a server-side claim link.

Encryption is on by default. Before encrypted QR codes are created, Text-o-Matic encrypts the complete prepared transfer locally in your browser using AES-GCM. The QR codes contain only encrypted transfer data plus the information needed to assemble the parts.

The separate transfer code is the key. Text-o-Matic generates a transfer code made of six randomly selected short words on the first device. That code is not included in any QR code and stays hidden on the QR screen until you choose to reveal it. After all QR codes are scanned, reveal the code and type the six words into the receiving Text-o-Matic page so it can decrypt the list locally. Spaces or hyphens are both accepted.

This helps if another QR app keeps scan history. A camera or QR-scanning app can still retain the QR contents according to its own settings, but with encryption enabled those contents are ciphertext rather than readable names, phone numbers, messages, or contact-card fields. Keep the transfer code separate from the QR codes until the transfer is complete.

You can turn encryption off. Unencrypted transfer skips the transfer-code step, but the QR codes then contain readable prepared contact and message data. Treat unencrypted QR codes like the list itself, especially if the camera or QR-scanning app may retain scan history.

The code includes the Text-o-Matic web address so your camera can open the app. Transfer data is placed after the # in that address (the URL fragment). Browsers do not send URL fragments as part of the normal request for the page. On the receiving device, a tiny first-party startup script captures and removes a Text-o-Matic transfer fragment before the app's third-party library code is allowed to execute. Browser history sync, session recovery, QR-scan history, or similar device features are still outside Text-o-Matic's control.

If the receiving device has not already cached Text-o-Matic, it may need internet access to load the app itself. That request is separate from transferring the list: Text-o-Matic does not upload a copy of the prepared list to store the transfer.

Multi-code transfers use temporary browser storage. Scanned parts are kept in IndexedDB until the transfer can be assembled. Encrypted parts remain encrypted there until you enter the transfer code. The temporary record is deleted when the transfer becomes the active texting queue; abandoned transfers expire after 24 hours of inactivity and are deleted the next time Text-o-Matic runs.

Easy way to think about encrypted transfer: the QR codes are locked envelopes carrying the data, while the separately displayed transfer code is the key. Neither one is uploaded to Text-o-Matic for storage.

Version …

Customize exception

Customize message

A little milestone

You've started a lot of texts

Donate