# @suaveplan/sms-providers - Universal SMS provider implementations for @suaveplan/sms. Real HTTP clients for Twilio (Basic auth + form-encoded + X-Twilio-Signature HMAC-SHA1), Vonage (HMAC-SHA256 webhook), MessageBird (AccessKey + JWT HS256 webhook), Plivo (Basic auth + X-Plivo-Signature-V3 HMAC-SHA256), and AWS SNS (inline SigV4 + RSA-SHA1 SNS notification verifier). Ships ProviderRegistry, httpRequest over injectable FetchLike, composeAbortSignal, SMS_PROVIDERS_CODES + SmsProvidersError, per-provider mapStatus, parseInbound, parseOptOutKeyword, and five OTel instruments. No provider SDK deps; every HTTP call flows through an injectable FetchLike seam. > Real provider clients for `@suaveplan/sms` — Twilio, Vonage, MessageBird, Plivo, AWS SNS. Universal (Bun, Node, browsers, Workers, Deno) over an injectable `FetchLike` seam; no provider SDKs in the dep tree. ## Documentation tree - [aws-sns](./src/aws-sns/aws-sns.md) — Real AWS SNS SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs `Action=Publish` form bodies to the r… - [aws-sns](./src/aws-sns/aws-sns.md) — Real AWS SNS SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs `Action=Publish` form bodies to the r… - [sigv4](./src/aws-sns/sigv4.md) — Inline AWS Signature V4 signer used by `AwsSnsProvider`. Replaces the `@aws-sdk/signature-v4` dependency tree (15+ MB)… - [crypto](./src/crypto/default-hmac.md) — Owns the package's default `HmacSign` implementation (routed through `globalThis.crypto.subtle`) and the small set of u… - [default-hmac](./src/crypto/default-hmac.md) — Owns the package's default `HmacSign` implementation (routed through `globalThis.crypto.subtle`) and the small set of u… - [error-codes](./src/error-codes/error-codes.md) — Defines the stable, namespaced error-code registry `SMS_PROVIDERS_CODES` and the `SmsProvidersError` discriminated subc… - [error-codes](./src/error-codes/error-codes.md) — Defines the stable, namespaced error-code registry `SMS_PROVIDERS_CODES` and the `SmsProvidersError` discriminated subc… - [http](./src/http/http.md) — Universal HTTP helper that every concrete provider client routes through. Wraps an injectable `FetchLike` seam, se… - [http](./src/http/http.md) — Universal HTTP helper that every concrete provider client routes through. Wraps an injectable `FetchLike` seam, se… - [keywords](./src/keywords/keywords.md) — Universal classifier mapping an inbound SMS body to the canonical `OptOutAction` enum used by `@suaveplan/sms`'s `OptOu… - [keywords](./src/keywords/keywords.md) — Universal classifier mapping an inbound SMS body to the canonical `OptOutAction` enum used by `@suaveplan/sms`'s `OptOu… - [messagebird](./src/messagebird/messagebird.md) — Real MessageBird SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/messages` with `Authorizatio… - [messagebird](./src/messagebird/messagebird.md) — Real MessageBird SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/messages` with `Authorizatio… - [otel](./src/otel/otel.md) — Owns the package's OpenTelemetry instrument set. Five instruments cover the public-surface operations every provider cl… - [otel](./src/otel/otel.md) — Owns the package's OpenTelemetry instrument set. Five instruments cover the public-surface operations every provider cl… - [plivo](./src/plivo/plivo.md) — Real Plivo SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/v1/Account/{AuthId}/Message/` auth… - [plivo](./src/plivo/plivo.md) — Real Plivo SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/v1/Account/{AuthId}/Message/` auth… - [registry](./src/registry/registry.md) — `ProviderRegistry` is a process-local, synchronous index of `SmsProvider` instances keyed by `provider.name`. Sibling p… - [registry](./src/registry/registry.md) — `ProviderRegistry` is a process-local, synchronous index of `SmsProvider` instances keyed by `provider.name`. Sibling p… - [twilio](./src/twilio/twilio.md) — Real Twilio SMS provider implementing the `SmsProvider` contract from `@suaveplan/sms`. Issues authenticated, form-enco… - [twilio](./src/twilio/twilio.md) — Real Twilio SMS provider implementing the `SmsProvider` contract from `@suaveplan/sms`. Issues authenticated, form-enco… - [types](./src/types/provider.md) — Defines the shared structural types every concrete provider class consumes: `FetchLike` (the injectable HTTP seam), `Hm… - [provider](./src/types/provider.md) — Defines the shared structural types every concrete provider class consumes: `FetchLike` (the injectable HTTP seam), `Hm… - [vonage](./src/vonage/vonage.md) — Real Vonage (formerly Nexmo) SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/sms/json` with `… - [vonage](./src/vonage/vonage.md) — Real Vonage (formerly Nexmo) SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/sms/json` with `… ## Highlights - **Five real provider clients**, every one implementing the `SmsProvider` contract from `@suaveplan/sms`: - `TwilioProvider` — `POST /Messages.json`, HTTP Basic auth, MMS `MediaUrl[]`, X-Twilio-Signature HMAC-SHA1 webhook verifier. - `VonageProvider` — `POST /sms/json`, body auth, auto `type=unicode`, HMAC-SHA256 webhook verifier. - `MessageBirdProvider` — `AccessKey` auth, JSON body, JWT HS256 webhook verifier with `iat`/`exp`/`nbf` enforcement + optional `payload_hash` binding. - `PlivoProvider` — Basic auth, JSON body, X-Plivo-Signature-V3 HMAC-SHA256 verifier. - `AwsSnsProvider` — inline AWS SigV4-signed `Action=Publish`, RSA-SHA1 SNS notification verifier with injectable callback. - **`ProviderRegistry`** indexes provider instances by name; strict register/lookup, frozen `list()` snapshot. - **`SmsProvidersError`** discriminated subclass of `SuaveplanError` with eight namespaced codes (`SMS_PROVIDERS.HTTP_ERROR`, `TIMEOUT`, `PARSE_ERROR`, `CONFIG_MISSING`, `SIGNATURE_INVALID`, `INVALID_NUMBER`, `PROVIDER_NOT_REGISTERED`, `PROVIDER_ALREADY_REGISTERED`). - **`parseOptOutKeyword`** universal CTIA/TCPA opt-in/opt-out/help keyword classifier. - **Five OTel instruments** — `send_total`, `webhook_total`, `signature_failures_total`, `send_duration_ms`, `webhook_verify_duration_ms`. - **Real `node:http` round-trip tests** per provider — no mocks; tests bind a server on a random port and inject a `FetchLike` pointing at it. ## Quick start ```ts import { AwsSnsProvider, MessageBirdProvider, PlivoProvider, ProviderRegistry, TwilioProvider, VonageProvider, } from "@suaveplan/sms-providers"; const registry = new ProviderRegistry(); registry.register(new TwilioProvider({ accountSid: "AC…", authToken: "…" })); registry.register(new VonageProvider({ apiKey: "k", apiSecret: "s" })); registry.register(new MessageBirdProvider({ accessKey: "K" })); registry.register(new PlivoProvider({ authId: "A", authToken: "T" })); registry.register( new AwsSnsProvider({ region: "us-east-1", accessKeyId: "AKIA…", secretAccessKey: "…", }) ); const twilio = registry.get("twilio"); const report = await twilio.send({ to: "+15551234567", from: "+15550000000", body: "Hello", }); console.log(report.messageId, report.status); ``` ## Webhook verification Every provider exposes a signature verifier matching its documented scheme: ```ts // Twilio HMAC-SHA1 over url + sorted form await twilio.verifyWebhookSignature(callbackUrl, params, signature); // Vonage HMAC-SHA256 hex over sorted &key=value (excluding sig) await vonage.verifyWebhookSignature(params, sigHex, signatureSecret); // MessageBird JWT HS256 with iat/exp/nbf window + payload_hash binding await mb.verifyWebhookSignature(jwt, signingKey, { nowMs, bodyHashHex }); // Plivo HMAC-SHA256 base64 over url + nonce + sorted key=value,... await plivo.verifyWebhookSignature(url, nonce, params, signature); // AWS SNS RSA-SHA1 — pass an injectable verifier callback await sns.verifyNotification(payload, async (pem, msg, sigB64) => { /* createVerify("RSA-SHA1")…verify(...) */ return true; }); ``` ## Tier `src/` ships zero `node:*` / `bun:*` / platform imports. Every HTTP call routes through an injectable `FetchLike`; every HMAC routes through an injectable `HmacSign` (defaulting to `globalThis.crypto.subtle`). Tests use real `node:http` + `node:crypto` from under `tests/setup/` and inside `*.test.ts` files only. ## Pre-publish gate ```bash bunx turbo lint typecheck test build --filter=@suaveplan/sms-providers bunx turbo test:coverage --filter=@suaveplan/sms-providers # 100/100/100/100 ```