Compliance checks

Every message you send, through the API, the console, a broadcast, a scheduled send, a recurring schedule or an automation, goes through the same checks in the same order before it reaches a carrier. You call POST /v1/messages; the checks are on by default.

A send that fails a check is blocked: it is not sent, it is not charged, the API says why, and the attempt is in your log with the reason.

+14155550123 Blocked Quiet hours +12125550148 Blocked Previously opted out +13125550190 Delivered

The checks, in order

The first check that fails is the one you hear about.

#CheckWhat it looks atReturnsfailure.codeRuns in
1Opt-out listThe recipient has opted out of your messages (replied STOP, or was imported or recorded as opted out). One list for the whole account, every number and customer.403 recipient_opted_outopted_outLive and sandbox. Send to +15005550011, or opt a number out with the consent API.
2DestinationThe number is in a US or Canadian area code. Other +1 regions are refused.403 forbiddendestination_not_allowedLive
3Sender registrationA US local number may text the public only once it is linked to an approved registration. Until then it reaches only your verified numbers.403 sender_not_registeredsender_not_registeredLive
4Per-recipient rateNo more than 30 messages to one number in an hour.429 rate_limitedrate_limitedLive. Send to +15005550012.
5Verified recipients (free accounts)A free account texts only numbers it has verified.403 recipient_not_verifiedrecipient_not_verifiedLive
6Quiet hoursA marketing message outside 8:00am to 9:00pm in the recipient's local time (read from the area code) is refused, or held until 8:00am when you ask for that. Transactional messages and one-time codes are not checked.403 quiet_hoursquiet_hoursLive and sandbox. Send to +15005550015 with category "marketing".
7Content screenProhibited content and public link shorteners are refused; restricted categories are refused until approved for your account.403 forbiddencontent_blockedLive
8Message allowanceYour plan's daily and monthly message allowance.429 quota_exceededquota_exceededLive and sandbox
9Spend limitThe monthly spending limit you set, if you set one.403 spend_limit_reachedspend_limit_reachedLive
10Prepaid creditsThe balance covers this message's price.402 insufficient_creditsinsufficient_creditsLive

After the last check the message goes to the carrier. What a carrier does with it afterwards (filtering, an unreachable phone) is a delivery failure, not a block.

This table is generated from the list the send pipeline is tested against, so it cannot fall out of step with the code.

What a blocked send looks like

The request answers with the status and code in the table, and a failure object you can show to a person:

json
{
  "error": {
    "code": "quiet_hours",
    "message": "It is 10:41pm for this recipient (America/New_York, from the number's area code). Marketing messages are sent between 8:00am and 9:00pm in the recipient's local time, so this one was not sent and nothing was charged. ...",
    "param": "to",
    "recipient_local_time": "22:41",
    "timezone": "America/New_York",
    "send_after": "2026-10-06T12:00:00.000Z",
    "category": "marketing",
    "category_basis": "request",
    "message_id": "msg_8Fq2ZkT0aLw4nXcV",
    "failure": { "code": "quiet_hours", "title": "Held back for quiet hours", "explanation": "...", "action": "Recommended: ...", "carrier_code": null },
    "request_id": "req_8Fq2ZkT0aLw4nXcV7pHs"
  }
}

The attempt is stored as a message with status: "blocked" and the same failure. Its price is 0, it counts toward no allowance or limit, and it is not part of the conversation with that number.

  • Console. Logs shows the status Blocked and the reason. Filter by Blocked, then by reason. Compliance shows blocked sends for the last 7 and 30 days, by reason.
  • API. GET /v1/messages?status=blocked lists them; &blocked_reason=quiet_hours narrows to one reason. failure_code matches failed and blocked messages alike.
  • Webhook. message.blocked, with reason, message_id and failure.
  • Export. The messages CSV has reason_code and reason columns.

Two refusals a looping client can repeat without end (the per-recipient rate and the message allowance) stop adding rows after 500 an hour per account. They are still refused and still counted.

Reason labels

The label is what Logs and the CSV show for each code.

failure.codeLabelStatus
carrier_filteredFiltered as spamfailed
unreachablePhone unreachablefailed
invalid_numberNumber not in servicefailed
opted_outPreviously opted outblocked
content_blockedContent policyblocked
rate_limitedSending too fastblocked
landlineNumber can't receive textsfailed
carrier_rejectedRejected by carrierfailed
spend_limit_reachedSpend limitblocked
insufficient_creditsOut of creditsblocked
sender_not_registeredSender not registeredblocked
quiet_hoursQuiet hoursblocked
recipient_not_verifiedNot a verified test recipientblocked
quota_exceededQuotablocked
destination_not_allowedDestination not servedblocked
topic_unsubscribedUnsubscribed from topicblocked
unknownNot deliveredfailed

Quiet hours

Marketing messages are sent between 8:00am and 9:00pm in the recipient's local time. That is the federal calling-time window for telephone solicitations. A marketing message outside it is refused, or held until 8:00am, depending on how it was sent.

Which messages are marketing

Only marketing messages are checked. In order:

  1. You said so. "category": "marketing", "transactional" or "otp" on the request always wins.
  2. A broadcast or batch is marketing unless it says otherwise.
  3. A message from a number registered for the Marketing use case is marketing.
  4. Everything else is transactional and is sent at any hour.

So if you send codes, receipts and alerts today and do nothing, nothing changes. A number registered as Mixed or Low-volume sends both kinds: label its marketing sends with "category": "marketing". The category is your statement about your own traffic. We do not read message text to decide it.

Never checked, whatever the category: verification codes (Verify), opt-out confirmations and HELP replies, a reply to someone who texted that number in the last 4 hours, and sends to your own verified test numbers.

Local time

We read the recipient's time zone from the area code of their number, for every US and Canadian area code. An area code tells us where a number was issued, not where the phone is: someone who moved and kept their number is read in their old time zone. It is the best signal available without location data, and it is an approximation.

  • An area code that spans two time zones is treated as quiet when it is quiet in either one.
  • A number we cannot place (toll-free, or outside the US and Canada) is not held. Nothing is guessed.
  • Daylight time, and the places that do not observe it (Arizona, Hawaii, Saskatchewan), follow the standard time zone database.

Refuse or hold

bash
curl https://api.joinsimplesms.com/v1/messages \
  -H "Authorization: Bearer $SIMPLESMS_API_KEY" -H "Content-Type: application/json" \
  -d '{"to": "+12125550148", "body": "20% off this week. Reply STOP to opt out.",
       "category": "marketing", "quiet_hours": "defer"}'
quiet_hoursIn quiet hours
"block"Refused with 403 quiet_hours. The body has recipient_local_time, timezone and send_after, the next allowed time.
"defer"Held. The response is 202 with a scheduled message set for the recipient's next 8:00am, plus a deferred object. It is listed and can be canceled like any scheduled message, and runs every check again when it is sent.

Without quiet_hours, the default depends on what is sending:

Sent byDefault
POST /v1/messages, the console composerRefuse
POST /v1/messages with scheduled_atRefuse when you schedule it, if the scheduled time is in quiet hours
Broadcasts and batchesHold, recipient by recipient
Recurring schedulesHold
AutomationsHold

A broadcast sent at 10:00pm Eastern reaches recipients on the West Coast right away and recipients on the East Coast at 8:00am their time.

A held message that is somehow still in quiet hours when it comes due is held again, at most 3 times, and then blocked.

Account default

Console → Compliance → Quiet hours sets the default for your account:

  • Refuse single sends, hold the rest (the default).
  • Hold everything until 8:00am, single sends included.
  • Off, if you have your own quiet-hours handling. This is never a default, it takes a second confirmation, and it is recorded in your audit log as quiet_hours.updated.

A quiet_hours value on a request always wins over the account default.

Test it

Quiet hours work the same with a test key. Two ways to trigger them:

  • Send to +15005550015 with "category": "marketing". For this number it is always night.
  • Send to any real-looking number with "category": "marketing": it is read against its area code, as in live mode. Nothing is delivered.

What quiet hours do not do

  • They do not apply any state rule. Some states set narrower hours, different weekend hours, or holiday restrictions for solicitations. Only the federal 8:00am to 9:00pm window is applied. If you send into such a state, schedule accordingly.
  • They do not know where a phone is, only where its number was issued.
  • They do not decide whether a message is marketing. You do.

What is not checked

These are not part of the pipeline. Nothing on this page should be read as covering them.

  • Reassigned numbers. We do not check whether a number has changed hands since the person gave you consent. No reassigned-number database is queried.
  • Litigator and complainer lists. We do not screen recipients against any list of known litigants or serial complainants.
  • Do-not-call registries. We do not check national or state do-not-call lists.
  • Consent itself. We store the opt-in evidence you give us and enforce opt-outs, but we cannot verify that a recipient agreed to hear from you. That stays your responsibility.
  • State-specific sending hours. Quiet hours use the federal 8:00am to 9:00pm window everywhere. Stricter state windows, weekend hours and holiday rules are not applied.
  • The recipient's actual location. Local time is read from the number's area code, which says where the number was issued, not where the phone is.

You remain responsible for having consent for every recipient, for classifying your traffic correctly, and for following the laws that apply to your messages. These checks enforce specific rules by default; they are not legal advice and do not make a messaging program compliant on their own. See the Messaging Policy.