# 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);
  },
});
```