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.
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"
- TwilioRejectedsender
- TelnyxAcceptedqueued
- 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
You get the result. Nothing else is tried.
The next provider is tried. A rate limit is retried on the same provider first.
Another provider would reject it too, so it stops here.
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.
See the cost before you send. One emoji can turn one segment into two.
Status webhooksDelivery updates from all six providers in one event shape, checked before your code reads them.
Inbound SMSReplies to your numbers arrive as message.received events.
STOP and HELPOpt-outs and help requests come through as their own events.
IdempotencyPass an idempotencyKey and a store, and a retried job returns the first result instead of texting twice.
TestingAn in-memory adapter, so tests never send a real text.