Sending overview
How sms.send() decides between accepted, rejected, and unknown, and which page covers each part of sending.
Every send ends in one of three outcomes, and SMS SDK handles each one differently.
Three outcomes
| Outcome | Meaning | What send() does |
|---|---|---|
| Accepted | The provider created or queued the message. This is not handset delivery. | Resolves with an SmsSendResult (handoff: "accepted"). |
| Rejected | The provider definitely did not create the message: bad credentials, an unregistered sender, an invalid number, an opt-out. | Throws ProviderRejectedError or a subclass, with retrySafe: true. Falls back if you enabled it. |
| Unknown | The request may have reached the provider, but the response does not show what happened: a timeout, a network error, a 5xx, a malformed success response. | Throws HandoffUnknownError with retrySafe: false. Never retries or falls back. |
Delivery is a separate question, answered later by a status webhook.
How a send runs
- Local checks. The recipient must be E.164, the sender valid for the primary adapter, and every field supported. A failure throws before any network request. See Validation and encoding.
- Idempotency. With a store and an
idempotencyKey, an earlier accepted send is returned instead of sending again. See Idempotency. - Policy. Your
beforeSendcallback can block the message, for example for a suppressed recipient. See Policy checks and hooks. - Requests. The primary adapter sends. A documented rate limit is retried on the same adapter. With
fallback: "on-known-rejection", an eligible rejection moves to the next adapter. See Retries and fallback. - Result. The first accepted response resolves the call. Anything else throws.
Choose a page
| You want to | Page |
|---|---|
| Send a message and read the result | Send your first SMS |
| Send from a short code, sender ID, or Messaging Service | Senders and E.164 numbers |
| Check a message before sending | Validation and encoding |
| Estimate segments | Segments and billing |
| Send media, schedule, or set an expiry | MMS, scheduling, and validity |
| Add a second provider | Retries and fallback |
| Avoid duplicate sends on retry | Idempotency |
| Block suppressed recipients, log attempts | Policy checks and hooks |
Best practices
- Pass an
idempotencyKeyfor every message. It should name the message, such asorder:123:shipped:v1, not the attempt. - On
HandoffUnknownError, check the provider console or wait for a status webhook before you send again. - Store
providerId. Status webhooks identify the message by it. - Keep fallback off until you need it. When you turn it on, give the second provider its own provisioned sender.