How to send SMS at scale without burning your number: timezone pacing and quiet hours
Mass-texting a national contact list from Salesforce fails without timezone-aware pacing. The fix is a metadata-driven engine that resolves every contact's local time and spreads delivery inside 8am to 8pm windows.
A national telehealth provider needed to text large segments of a list with well over a hundred thousand contacts. The first naive attempt at this kind of thing always looks the same: loop over the list, fire messages as fast as the API allows. Then carriers throttle the traffic, deliverability craters, and people in one timezone get marketing texts before their alarm goes off.
The short answer
Every contact gets a resolved IANA timezone, every message gets scheduled into that contact’s local 8am-to-8pm window, and a pacing engine spreads sends across the window at a controlled per-minute rate. All of it lives in Salesforce as configuration, not code constants.
Resolving timezone from a phone number
You rarely have a clean timezone field. The system resolves in tiers:
- An explicit timezone on the contact, if someone captured one.
- The contact’s state, where present.
- The area code, mapped through a custom metadata table of several hundred North American Numbering Plan area codes to IANA timezones.
- A conservative default for the unresolvable remainder.
The area-code table is custom metadata, which means it deploys like code but edits like data. When a new area code appears, an admin adds a row; nothing is recompiled.
The pacing engine
A blast is a record, not a button press. It carries a status machine (queued, sending, complete, canceled), a target throughput per minute, and per-timezone send windows. The engine wakes on a schedule, computes which timezones are currently inside their local window, and releases the next tranche for those zones only. East-coast contacts finish their day before west-coast contacts begin; nobody gets a 6am text.
Two design choices proved their worth quickly:
- Every message links back to its blast. Progress, failure counts, and cancellation are per-blast queries, and stopping a misconfigured blast mid-flight is one field update.
- Reusable cohorts. Audience filters are saved as records (insurance status, engagement recency, lifecycle stage), so the next campaign starts from a tested segment instead of a fresh ad-hoc query.
Results worth copying
Delivery rates recovered to normal once pacing replaced bursting, complaint rates fell, and the operations team gained something they never had: the ability to answer “how is the 2pm blast doing?” with a record page instead of a guess. Test coverage across the engine sits above ninety percent, because a system that texts the public is not a place for surprises.
If you are building this
Start with the timezone table; everything else depends on it. Put the quiet-hour window in configuration with per-state overrides, because the rules are not uniform. And build cancellation before you build send, because the day you need to stop a blast, you need it in seconds.
Common questions
Why do SMS blasts to large lists fail or get numbers flagged?
Two reasons: carriers rate-limit and filter senders that burst traffic, and messages delivered outside the recipient's local daytime hours generate complaints. Both problems require pacing delivery by each contact's timezone.
How do you know a contact's timezone if you only have a phone number?
Resolve it in tiers: an explicit timezone field if present, then the contact's state, then the phone number's area code mapped against the North American Numbering Plan. A few hundred area-code mappings cover the U.S. accurately enough for send windows.
Is 8am to 8pm local time a legal requirement for marketing texts?
Quiet-hour rules come from TCPA-adjacent regulations and carrier policies, and several states enforce stricter windows. Treating 8am to 8pm local as the floor, with per-state overrides where required, keeps a program defensible. This is engineering guidance, not legal advice.