Skip to content
SMS SDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

Fallback and retries

Retry rate limits and fall back to a second provider, only when the first provider proved it did not take the message.

Get a message through when a provider rate limits you or refuses your sender, without texting anyone twice. The SDK retries or moves to another provider only when the provider proved it did not take the message.

Fall back to a second provider

List adapters in priority order and set fallback: "on-known-rejection":

import { createSmsClient } from "@opencoredev/sms-sdk";
import { telnyx } from "@opencoredev/sms-sdk/telnyx";
import { twilio } from "@opencoredev/sms-sdk/twilio";

const sms = createSmsClient({
  adapters: [
    twilio({
      accountSid: process.env.TWILIO_ACCOUNT_SID ?? "",
      authToken: process.env.TWILIO_AUTH_TOKEN ?? "",
      from: "+15550100001",
    }),
    telnyx({ apiKey: process.env.TELNYX_API_KEY ?? "", from: "+15550100002" }),
  ],
  fallback: "on-known-rejection",
  retry: { maxAttempts: 3 },
});

const result = await sms.send({ to: "+14155550123", body: "Your code is 482913" });
console.log(result.provider, result.attemptedProviders); // "telnyx" ["twilio", "telnyx"] if Twilio refused

Twilio sends first. If Twilio refuses for a reason tied to that account, such as bad credentials or an unregistered sender, Telnyx sends next. Without fallback, only the first adapter is used.

Each adapter sends from its own from. With fallback, leave from off the message unless every adapter can send from that exact sender.

Which rejections fall back

A rejection falls back when the problem belongs to one provider account. It stops when any provider would refuse the same message.

Category Falls back Why Examples
auth Yes Credentials are wrong for this account only. 401, Twilio 20003, Telnyx 10009
rate_limited Yes, after retries on the same provider This account is throttled. Twilio 20429, Telnyx 10011 or 40318, Vonage 1000 or 1241
sender Yes This account’s sender cannot be used here. Twilio 21606, Telnyx 40305, Vonage 1420
account Yes Balance, trial, country permission, or spend limit. Twilio 21408, Telnyx 40333, Vonage 402
recipient No Another provider would reject the number too. Twilio 21211, Telnyx 40310, Vonage 1430
compliance Never The recipient opted out or the content was blocked. A second provider must not be used to get around that. Twilio 21610, Telnyx 40300, Vonage 1240
request No The request itself is wrong. Any other 4xx

Providers differ in how precisely their errors map. Plivo documents no error codes, so only its 401 and 429 are classified and every other 4xx is request. Each provider page lists its codes. In code, error.fallbackEligible and isFallbackEligible(category) give the answer.

Fallback never runs after an accepted or unknown outcome, or after beforeSend rejects. It skips adapters that cannot send the message’s fields or sender type, but the first adapter must support them all.

When every adapter rejects, send() throws AllProvidersRejectedError with one rejection per adapter in errors. When the chain stops at a rejection that does not fall back, that rejection is thrown directly.

Retries on the same provider

Only a proven rate limit (category rate_limited) is retried. By default each adapter gets 2 requests per send.

retry option Default Meaning
maxAttempts 2 Requests per adapter, counting the first. 1 turns retries off.
baseDelayMs 500 Base for exponential backoff with full jitter: a random wait up to baseDelayMs * 2^(attempt - 1).
maxDelayMs 10000 Longest wait. If the provider’s Retry-After is longer, the SDK stops retrying that adapter instead of waiting.

A provider Retry-After that fits within maxDelayMs is honored. Aborting during the wait throws SendAbortedError, which is retry safe.

Two cases look retryable but are not:

  • 5xx responses. A provider can return a 5xx after it created the message, even when it says “try again later”.
  • A bare 429. A 429 counts as a rate limit only when its body carries the provider’s documented rate-limit error, such as Twilio 20429. A 429 from a proxy or CDN says nothing about the message, so it is an unknown outcome.

Before you turn fallback on

Each provider account in the chain needs:

  • Its own registered sender. A 10DLC registration on Twilio does not cover your Telnyx number. See Registration and consent.
  • Webhook URLs, so status updates and replies arrive from both. See Webhooks.
  • Your shared opt-out list. Twilio records a STOP sent to your Twilio number, and Telnyx never sees it. Check your own list in beforeSend. See STOP, HELP, and opt-outs.

Next steps