# Twilio SMS sending and receiving - Package: @bijectionhq/twilio - Category: Messaging - Status: Preview - License: Apache-2.0 - Bijection source (repository access required): https://github.com/bijectionhq/twilio/tree/0308857a45349e59d3be429350720779acbabc88 - Upstream: https://github.com/convex/twilio/tree/54df32e8c37aeb9692f19efa3b8c3e2cb3719e00 - Documentation: https://docs.bijection.com/components/using - Directory: https://bijection.com/components/twilio ## Get the source Bijection repository access is required. Request access at https://bijection.com/contact. ```sh git clone https://github.com/bijectionhq/twilio.git cd twilio git checkout 0308857a45349e59d3be429350720779acbabc88 ``` ## Before using this component Read the package README at the recorded revision for setup, dependencies, and API examples. Upstream documentation describes the original package and may differ from the Bijection adaptation. This is a source candidate for evaluation. Catalog inclusion does not establish npm availability or runtime qualification. Check compatibility with your SDK and deployment before adoption. External providers run their own compute. ## Upstream package documentation Recorded 2026-10-23. This describes @convex-dev/twilio; check the Bijection adaptation separately. This Twilio component integrates SMS messaging capabilities into your Convex app. It handles both sending outgoing messages through Twilio's API and receiving incoming messages via webhook handlers. The component automatically stores message status updates and provides query functions to retrieve message history, enabling you to build SMS-driven features without managing webhook infrastructure or message persistence. ### Use cases - Build notification systems that send SMS alerts for order updates, appointment reminders, or security events - Create two-way SMS customer support where users can text your business number and agents can respond - Implement SMS-based authentication flows with verification codes sent through Twilio - Build SMS marketing campaigns that track delivery status and handle opt-out responses - Create SMS chatbots that process incoming messages and respond based on business logic ### How it works The component requires Twilio API credentials and registers HTTP webhook routes (`/twilio/message-status` and `/twilio/incoming-message`) in your Convex app. You instantiate a Twilio client in your functions and call `sendMessage()` from actions to send SMS messages. For incoming messages, you associate your Twilio phone number with the webhook URL either through the Twilio console or by calling `registerIncomingSmsHandler()`. The component automatically stores all messages in a database table and provides query methods like `list()`, `getMessageBySid()`, and `getMessagesByCounterparty()`. You can execute custom logic on incoming messages by setting an `incomingMessageCallback` that runs in the same transaction as message storage. The component tracks message delivery status updates from Twilio, so you can monitor whether SMS messages were successfully delivered. ### Upstream README Send and receive SMS messages in your Convex app using Twilio. ```ts import { Twilio } from "@convex-dev/twilio"; import { components } from "./_generated/api"; export const twilio = new Twilio(components.twilio, { defaultFrom: process.env.TWILIO_PHONE_NUMBER!, }); export const sendSms = internalAction({ handler: async (ctx, args) => { return await twilio.sendMessage(ctx, { to: "+14151234567", body: "Hello, world!", }); }, }); ``` ## Prerequisites ### Twilio Phone Number Create a Twilio account and, if you haven't already, create a [Twilio Phone Number](https://www.twilio.com/docs/phone-numbers). Note the **Phone Number SID** of the phone number you'll be using, you'll need it in a moment. ### Convex App You'll need a Convex App to use the component. Follow any of the [Convex quickstarts](https://docs.convex.dev/home) to set one up. ## Installation Install the component package: ```ts npm install @convex-dev/twilio ``` Create a `convex.config.ts` file in your app's `convex/` folder and install the component by calling `use`: ```ts // convex/convex.config.ts import { defineApp } from "convex/server"; import twilio from "@convex-dev/twilio/convex.config.js"; const app = defineApp(); app.use(twilio); export default app; ``` Set your API credentials: ```sh npx convex env set TWILIO_ACCOUNT_SID=ACxxxxx npx convex env set TWILIO_AUTH_TOKEN=xxxxx ``` Instantiate a Twilio Component client in a file in your app's `convex/` folder: ```ts // convex/example.ts import { Twilio } from "@convex-dev/twilio"; import { components } from "./_generated/api"; export const twilio = new Twilio(components.twilio, { // optionally pass in the default "from" phone number you'll be using // this must be a phone number you've created with Twilio defaultFrom: process.env.TWILIO_PHONE_NUMBER!, }); ``` Register Twilio webhook handlers by creating an `http.ts` file in your `convex/` folder and use the client you've exported above: ```ts // http.ts import { twilio } from "./example"; import { httpRouter } from "convex/server"; const http = httpRouter(); // this call registers the routes necessary for the component twilio.registerRoutes(http); export default http; ``` ## Sending Messages To send a message use the Convex action `sendMessage`: ```ts // convex/messages.ts import { v } from "convex/values"; import { internalAction } from "./_generated/server"; export const sendSms = internalAction({ handler: async (ctx, args) => { const status = await twilio.sendMessage(ctx, { to: "+14158675309", body: "Hey Jenny", }); }, }); ``` By querying the message (see [below](#querying-messages)) you can check for the status ([Twilio Statuses](https://www.twilio.com/docs/messaging/api/message-resource#message-status-values)). The component subscribes to status updates and writes the most up-to-date status into the database. ## Receiving Messages To receive messages, you will associate a webhook handler provided by the component with the Twilio phone number you'd like to use. `twilio.registerRoutes` registers two webhook HTTP handlers in your your Convex app's deployment: - `YOUR_CONVEX_SITE_URL/twilio/message-status` - capture and store delivery status of messages you **send**. - `YOUR_CONVEX_SITE_URL/twilio/incoming-message` - capture and store messages **sent to** your Twilio phone number. Note: You may pass a custom `httpPrefix` to `Twilio` if you want to route Twilio endpoints somewhere other than `YOUR_CONVEX_SITE_URL/twilio/*`. For instance, to route to `YOUR_CONVEX_SITE_URL/custom-twilio/message-status`, set: ```ts export const twilio = new Twilio(components.twilio, { httpPrefix: "/custom-twilio", }); ``` You can associate it with your Twilio phone number in two ways: 1. Using the [Twilio console](https://console.twilio.com/) in the "Configure" tab of the phone number, under "Messaging Configuration" -> "A messsage comes in" -> "URL". 2. By calling `registerIncomingSmsHandler` exposed by the component client, passing it the phone number's SID: ```ts export const registerIncomingSmsHandler = internalAction({ args: {}, handler: async (ctx) => { return await twilio.registerIncomingSmsHandler(ctx, { sid: "YOUR_TWILIO_PHONE_NUMBER_SID", }); }, }); ``` Once it is configured, incoming messages will be captured by the component and logged in the `messages` table. You can execute your own logic upon receiving an incoming message, by providing a callback when instantiating the Twilio Component client: ```ts // convex/example.ts import { Twilio, messageValidator } from "@convex-dev/twilio"; const twilio = new Twilio(components.twilio); twilio.incomingMessageCallback = internal.example.handleIncomingMessage; export const handleIncomingMessage = internalMutation({ args: { message: messageValidator, }, handler: async (ctx, message) => { // Use ctx here to update the database or schedule other actions. // This is in the same transaction as the component's message insertion. console.log("Incoming message", message); }, }); ``` If the `handleIncomingMessage` callback throws an error, the message will not be saved and the webhook will throw an error. Twilio [does not retry webhook requests](https://www.twilio.com/docs/usage/security/availability-reliability), but you can replay them manually from the Twilio "Error logs" console. ## Querying Messages To list all the mssages, use the `list` method in your Convex function. To list all the incoming or outgoing messages, use `listIncoming` and `listOutgoing` methods: ```ts // convex/messages.ts // ... export const list = query({ args: {}, handler: async (ctx) => { const allMessages = await twilio.list(ctx); const receivedMessages = await twilio.listIncoming(ctx); const sentMessages = await twilio.listOutgoing(ctx); return { allMessages, receivedMessages, sentMessages }; }, }); ``` To get a single message by its sid, use `getMessageBySid`: ```ts export const getMessageBySid = query({ args: { sid: v.string(), }, handler: async (ctx, args) => { return await twilio.getMessageBySid(ctx, args); }, }); ``` Get messages by the "to" phone number: ```ts export const getMessagesTo = query({ args: { to: v.string(), }, handler: async (ctx, args) => { return await twilio.getMessagesTo(ctx, args); }, }); ``` Get messages by the "from" phone number: ```ts export const getMessagesFrom = query({ args: { from: v.string(), }, handler: async (ctx, args) => { return await twilio.getMessagesFrom(ctx, args); }, }); ``` You can also get all messages to and from a particular number: ```ts export const getMessagesByCounterparty = query({ args: { from: v.string(), }, handler: async (ctx, args) => { return await twilio.getMessagesByCounterparty(ctx, args); }, }); ```