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

One send() for every SMS provider.

A typed TypeScript client for Twilio, Telnyx, Plivo, Vonage, Bandwidth, and Sinch. It tells you which provider took each text, or why none did.

Read the docs
send.tsexample run
import { createSmsClient } from "@opencoredev/sms-sdk";import { twilio } from "@opencoredev/sms-sdk/twilio";import { telnyx } from "@opencoredev/sms-sdk/telnyx"; 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",}); const result = await sms.send({  to: "+14155550123",  body: "Your order has shipped.",}); console.log(result.provider); // "telnyx"
+1 (415) 555-0123sms_01JQ8Z3T7K
Your order has shipped.
  1. TwilioRejectedsender
  2. TelnyxAcceptedqueued
  3. WebhookDeliveredmessage.delivered

Use the provider you already pay for.

Every provider is a separate import that returns the same result, so moving from one to another doesn't touch the rest of your code.

If one provider says no, the next one gets a try.

Fallback is off until you set fallback: "on-known-rejection". Then it only moves on when a rejection is safe to send elsewhere. When the outcome is unknown, it stops, so nobody gets the same text twice. How fallback decides

Acceptedresolves

You get the result. Nothing else is tried.

Blocked sender, auth, or rate limitfalls back

The next provider is tried. A rate limit is retried on the same provider first.

Bad number or opted outthrows

Another provider would reject it too, so it stops here.

Timeout or unknown outcomethrows

The text may already be on its way, so it is never retried.

Webhooks, opt-outs, and tests are already built.

The parts every SMS integration ends up writing by hand.