SMS SDK is an open-source TypeScript library for sending and receiving transactional SMS through Twilio, Telnyx, Plivo, Vonage, Bandwidth, or Sinch with one typed API. Switch providers by changing one import, add a second provider as a fallback, and verify every provider's webhooks the same way.
It never texts anyone twice: it falls back only when a provider proves it refused the message, and stops when the outcome is unknown.
[Start with the quick start](https://www.sms-sdk.com/getting-started/quick-start) · [Read the docs](https://www.sms-sdk.com) · [Install from npm](https://www.npmjs.com/package/@opencoredev/sms-sdk)
- Adapters for Twilio, Telnyx, Plivo, Vonage, Bandwidth, and Sinch, each behind its own subpath import
- Opt-in fallback to the next provider after a known rejection, with rate-limit retries that honor `Retry-After`
- Typed errors that say whether sending again is safe, and idempotency keys that stop duplicate sends
- Verified, normalized webhooks for delivery status, replies, and STOP/HELP opt-outs
- A `memory()` adapter and signed webhook builders for tests that never touch the network
- Zero runtime dependencies, ESM, Node.js 20+ and Bun
## Install
SMS SDK runs in server-side code. Keep provider credentials and webhook secrets out of browser bundles.
```bash
npm install @opencoredev/sms-sdk
# or
bun add @opencoredev/sms-sdk
```
## Usage
Create a client with one or more adapters and call `send()`:
```ts
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" when Twilio refused the sender
```
`send()` resolves only when a provider accepted the message. A refusal throws `ProviderRejectedError` with `retrySafe: true`. A timeout or unreadable response throws `HandoffUnknownError` with `retrySafe: false`, and the SDK neither retries nor falls back, because the text may already be on its way. [How sending works](https://www.sms-sdk.com/getting-started/how-sending-works) covers every outcome and default.
No provider account yet? Swap in `memory()` from `@opencoredev/sms-sdk/testing`. It records every send and calls nothing.
## Providers
| Provider | Status | MMS | Scheduling | Webhook verification |
| --- | --- | --- | --- | --- |
| [Twilio](https://www.sms-sdk.com/providers/twilio) | supported | yes | yes (Messaging Service) | HMAC-SHA1 |
| [Telnyx](https://www.sms-sdk.com/providers/telnyx) | supported | yes | yes | Ed25519 |
| [Plivo](https://www.sms-sdk.com/providers/plivo) | partial | yes | no | HMAC-SHA256 V2 |
| [Vonage](https://www.sms-sdk.com/providers/vonage) | partial | no | no | HS256 JWT |
| [Bandwidth](https://www.sms-sdk.com/providers/bandwidth) | partial | no | no | Basic credentials |
| [Sinch](https://www.sms-sdk.com/providers/sinch) | partial | no | yes | Basic or HMAC |
Partial means part of the provider's behavior is undocumented, so fewer rejections can be recognized for fallback. [`PROVIDERS.md`](packages/sms-sdk/PROVIDERS.md) lists every provider fact the SDK relies on, its source, and what could not be confirmed. [Choose a provider](https://www.sms-sdk.com/providers/overview) has the full capability matrix.
## Webhooks
One parser verifies the request before reading it and returns the same event shape for every provider:
```ts
import { parseSmsWebhook } from "@opencoredev/sms-sdk/webhooks";
const event = await parseSmsWebhook({
provider: "telnyx",
request,
credentials: { publicKey: process.env.TELNYX_PUBLIC_KEY! },
});
// event.type: "message.delivered", "message.received", "recipient.opted_out", ...
```
See [Receive webhooks](https://www.sms-sdk.com/receiving/webhooks) for Next.js, Hono, and Bun handlers.
## Documentation
Full documentation lives at **[www.sms-sdk.com](https://www.sms-sdk.com)**. Coding agents can read it as Markdown, through [`llms.txt`](https://www.sms-sdk.com/llms.txt), or with the MCP server:
```bash
claude mcp add --transport http sms-sdk https://www.sms-sdk.com/mcp
```
Good places to start:
- [Quick start](https://www.sms-sdk.com/getting-started/quick-start)
- [Fallback and retries](https://www.sms-sdk.com/sending/fallback-and-retries)
- [Prevent duplicate sends](https://www.sms-sdk.com/sending/idempotency)
- [Test without a provider](https://www.sms-sdk.com/testing/local-testing)
- [Switch providers](https://www.sms-sdk.com/migration/switch-providers)
## Contributing
```bash
bun install
bun run test # SDK tests
bun run typecheck # SDK types
bun run dev # docs site
```
Every change to the package ships with a changeset (`bun run changeset`). See [Contributing and support](https://www.sms-sdk.com/advanced/contributing) for the release flow, adding a provider, and reporting security issues.
## Sponsors
Want your logo here? **[Become a sponsor →](https://github.com/sponsors/opencoredev)**