npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@telnyx/chat-sdk-adapter

v0.1.0

Published

Vercel Chat SDK adapter for Telnyx SMS/MMS

Downloads

70

Readme

@telnyx/chat-sdk-adapter

npm version npm downloads license

Vercel Chat SDK adapter for Telnyx SMS/MMS. Bidirectional: receive messages via Telnyx webhooks (Ed25519-verified), send messages via the Telnyx Messaging API.

Install

pnpm add @telnyx/chat-sdk-adapter chat

Also install a state adapter — @chat-adapter/state-memory for dev, @chat-adapter/state-redis for production:

pnpm add @chat-adapter/state-memory

Requires Node.js >=18.

Quick Start

import { createTelnyxAdapter } from "@telnyx/chat-sdk-adapter";
import { createMemoryState } from "@chat-adapter/state-memory";
import { Chat } from "chat";

const telnyx = createTelnyxAdapter({
  // apiKey:     "KEY..."     // or set TELNYX_API_KEY
  // phoneNumber: "+15551234567" // or set TELNYX_FROM_NUMBER
  // publicKey:  "abc123..."   // or set TELNYX_PUBLIC_KEY
  messagingProfileId: "40017a7b-...",
});

const chat = new Chat({
  userName: "sms-bot",
  adapters: { telnyx },
  state: createMemoryState(),
});

// New inbound SMS (new thread)
chat.onNewMention(async (thread, message) => {
  await thread.subscribe();
  await thread.post(`Got your text: ${message.text}`);
});

// Follow-up SMS in a subscribed thread
chat.onSubscribedMessage(async (thread, message) => {
  await thread.post(`Reply: ${message.text}`);
});

await chat.initialize();

In your webhook route handler, forward the request to adapter.handleWebhook:

// app/api/webhooks/telnyx/route.ts (Next.js App Router)
export async function POST(request: Request) {
  return telnyx.handleWebhook(request);
}

See examples/basic for a full runnable server.

Configuration

Environment Variables

| Variable | Required | Description | |----------|----------|-------------| | TELNYX_API_KEY | Yes | Telnyx API key (overridden by config.apiKey) | | TELNYX_FROM_NUMBER | Yes | E.164 phone number you send from, e.g. +15551234567 (overridden by config.phoneNumber) | | TELNYX_PUBLIC_KEY | Recommended | Ed25519 public key for webhook verification. Accepts either the base64 form shown in Mission Control or a 64-char hex form — the adapter auto-detects. Overridden by config.publicKey. If unset, webhook signatures are not verified — do not run that way in production. | | BOT_USERNAME | No | Bot display name. Defaults to "bot" (overridden by config.userName). |

TelnyxAdapterConfig

interface TelnyxAdapterConfig {
  /** Telnyx API key. Defaults to TELNYX_API_KEY env var. */
  apiKey?: string;
  /** Telnyx phone number to send from (E.164). Defaults to TELNYX_FROM_NUMBER env var. */
  phoneNumber?: string;
  /** Ed25519 public key for webhook signature verification (base64 or hex — auto-detected). Defaults to TELNYX_PUBLIC_KEY env var. */
  publicKey?: string;
  /** Telnyx messaging profile ID. Optional, but strongly recommended — see below. */
  messagingProfileId?: string;
  /** Bot username used for mention detection. Defaults to BOT_USERNAME env var, then "bot". */
  userName?: string;
  /** Additional tags merged into the `tags` array on every outbound message, after the adapter-attribution tags. */
  extraTags?: string[];
  /** Opt out of adapter-attribution tags on outbound messages. Defaults to false. */
  disableAttributionTags?: boolean;
  /** Logger instance. Defaults to ConsoleLogger. */
  logger?: Logger;
}

Recommended: Dedicated Messaging Profile

Create a dedicated Telnyx Messaging Profile for your chat bot and pass its ID as messagingProfileId. We recommend prefixing the profile name with [Chat SDK] (for example, [Chat SDK] support-bot-prod) so it is easy to find in the Mission Control portal and in usage exports.

Benefits of a dedicated profile:

  • Per-profile usage analytics, so bot traffic is separated from the rest of your Telnyx account
  • Per-profile spend limits, which let you cap bot spend without affecting other workloads
  • Isolated webhook URL and failover webhook configuration
  • Isolated webhook public key — the key you pass as publicKey is the one on the profile, not on the account

The adapter attaches messaging_profile_id to every outbound request when this is set.

Capabilities

| Capability | Supported | Notes | |---|---|---| | Inbound SMS/MMS | Yes | via webhooks, Ed25519-signed | | Outbound SMS | Yes | via POST /v2/messages | | Outbound MMS | Yes | auto-upgrade when a posted message has attachments with public URLs | | Ed25519 webhook verification | Yes | with a 300-second replay-attack window | | Rate-limit handling | Yes | 429 responses surface as AdapterRateLimitError with Retry-After | | Direct messages | Yes | isDM() is always true; a thread is one pair of phone numbers | | Typing indicators | No | no-op; SMS has no typing concept | | Reactions | No | NotImplementedError; SMS has no reactions | | Edit / delete | No | NotImplementedError; SMS messages are immutable once delivered | | Message history | No | Telnyx has no thread-based history API; rely on the Chat SDK state adapter |

Attribution

The adapter advertises itself in three places so ecosystem usage is observable:

  1. User-Agent header on every outbound API call: @telnyx/chat-sdk-adapter/<version> (vercel-chat-sdk).
  2. tags on every outbound message: ["vercel-chat-sdk", "vercel-chat-sdk:<version>"], merged with any user-supplied tags.
  3. Dedicated Messaging Profile convention (see the section above) — the primary attribution signal for Telnyx-side usage analytics.

Where tags show up on Telnyx

Attribution tags are visible in Telnyx webhook event payloads (message.sent, message.finalized, message.received) in the data.payload.tags field. They are accepted by POST /v2/messages but are not surfaced in the GET /v2/messages/{id} response. Tag-based attribution is therefore a webhook/event-stream signal, not a lookup-API signal.

Merging and opting out

User-supplied tags are merged after the attribution tags. To opt out of attribution tags entirely, set disableAttributionTags: true; extraTags is unaffected.

const telnyx = createTelnyxAdapter({
  extraTags: ["env:prod", "team:support"],
  // disableAttributionTags: true,
});

The adapter re-exports the constants it uses so your own logging can stay consistent:

import {
  ADAPTER_NAME,
  ADAPTER_VERSION,
  USER_AGENT,
  ATTRIBUTION_TAGS,
} from "@telnyx/chat-sdk-adapter";

Webhook Setup

  1. Open the Telnyx Mission Control portal and navigate to the messaging profile you created for the bot.
  2. In Inbound Settings, set the Webhook URL to your server's webhook endpoint (for example, https://bot.example.com/api/webhooks/telnyx).
  3. Set Webhook API version to 2.
  4. Under Messaging -> Security, copy the Public Key for the profile and set it as TELNYX_PUBLIC_KEY (or pass it as publicKey). Telnyx shows this key in base64; the adapter accepts it as-is. This is the key the adapter uses to verify incoming webhook signatures.
  5. Assign one or more phone numbers to the profile. The number you set as TELNYX_FROM_NUMBER must belong to this profile.

Incoming webhooks are validated against the telnyx-signature-ed25519 and telnyx-timestamp headers. Requests with a timestamp older than 300 seconds, or with an invalid signature, are rejected with 401.

Thread Model

  • A thread is a pair of E.164 phone numbers. The thread ID format is telnyx:<bot-number>:<user-number>, for example telnyx:+15551234567:+15559876543.
  • isDM() always returns true.
  • openDM(phoneNumber) returns the thread ID for a given recipient number so you can post proactively.
  • fetchMessages() returns an empty list — Telnyx does not expose a thread-scoped history API. Use a durable Chat SDK state adapter (for example Redis) to persist subscriptions across restarts.

Limitations

  • No typing indicators. startTyping() is a no-op because SMS does not support them.
  • No reactions. addReaction / removeReaction throw NotImplementedError.
  • No edit or delete. SMS messages are final once sent. editMessage / deleteMessage throw NotImplementedError.
  • No server-side message history. fetchMessages returns an empty result.
  • SMS length cap. Outbound text is truncated at 1600 characters before being handed to Telnyx, which will segment it into concatenated SMS parts.
  • Webhook verification is skipped when publicKey is unset. Set it in production.

Credits

Originally prototyped by Hayden Bleasel (@haydenbleasel) in vercel/chat#198; thanks to Vercel for the Chat SDK.

License

MIT — see LICENSE.