> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getdial.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getdial.ai/_mcp/server.

# Vercel Chat SDK

> Add Dial as a channel to any bot built on Vercel's Chat SDK — the same handler answers SMS, MMS, iMessage, and inbound voice through one adapter.

[**Vercel Chat SDK**](https://chat-sdk.dev) is a TypeScript framework for building multi-channel bots: one bot handler responds to messages across Slack, Teams, Discord, and other channels through adapters. The `@getdial/chat-sdk-adapter` package plugs **Dial** into that adapter surface so the same bot handler also answers phone traffic — SMS, MMS, iMessage, and inbound voice-call transcripts — with replies sent back over the phone through Dial.

Written and maintained by Dial. Published on npm as [`@getdial/chat-sdk-adapter`](https://www.npmjs.com/package/@getdial/chat-sdk-adapter). Source at [`GetDial-AI/chat-sdk-adapter`](https://github.com/GetDial-AI/chat-sdk-adapter).

## Install

```bash
npm install @getdial/chat-sdk-adapter chat
```

`chat` is Vercel's Chat SDK; both packages are required. Node 18+.

## Usage

```typescript
import { Chat } from "chat";
import { createMemoryState } from "@chat-adapter/state-memory";
import { createDialAdapter } from "@getdial/chat-sdk-adapter";

const bot = new Chat({
  userName: "mybot",
  adapters: {
    dial: createDialAdapter(),  // reads DIAL_* env vars; explicit config wins
  },
  state: createMemoryState(),   // swap for Redis/Postgres in production
});

bot.onNewMention(async (thread, message) => {
  await thread.post(`heard you: ${message.text}`);
});

// Bind the webhook endpoint on whichever HTTP framework you use:
app.post("/webhook/dial", (req) => bot.webhooks.dial(req));
```

The `bot.webhooks.dial(request)` call routes the inbound HTTP request through the adapter's signature verification, envelope parsing, and Chat SDK's per-thread state — your handler runs identically to a Slack or Teams mention.

## Configuration

`createDialAdapter(config?)` reads the environment when a field is omitted. Explicit config values win.

| Field           | Env var               | Required | What it is                                                                                                                                                                                                    |
| --------------- | --------------------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `apiKey`        | `DIAL_API_KEY`        |     ✅    | Dial API key (`sk_live_…`). Mint it from the [dashboard](https://getdial.ai/dashboard).                                                                                                                       |
| `fromNumberId`  | `DIAL_FROM_NUMBER_ID` |     ✅    | Dial's ID of the phone number the bot sends **from**. Copy it from the [Phone Numbers page](https://getdial.ai/dashboard/numbers), or `dial number list --json`.                                              |
| `webhookSecret` | `DIAL_WEBHOOK_SECRET` |   prod   | HMAC-SHA256 signing secret Dial issued when the webhook subscription was created. Required in production; when set, incoming requests are verified against `X-Dial-Signature` and rejected `401` on mismatch. |
| `apiBaseUrl`    | `DIAL_API_URL`        |          | Override the Dial API host. Defaults to `https://api.getdial.ai`.                                                                                                                                             |
| `botName`       | `BOT_USERNAME`        |          | Display name Chat SDK uses for the bot. Defaults to `"bot"`.                                                                                                                                                  |
| `logger`        | —                     |          | Chat SDK `Logger` instance. Defaults to `ConsoleLogger("info").child("dial")`.                                                                                                                                |

## Webhook setup

Point a Dial webhook subscription at the endpoint you handed to `bot.webhooks.dial`. Create the subscription from the [Webhooks page](https://getdial.ai/dashboard/webhooks) or over the API:

```bash
curl -X POST https://api.getdial.ai/api/v1/webhooks \
  -H "Authorization: Bearer $DIAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "targetUrl": "https://your-bot.example.com/webhook/dial", "eventTypes": ["*"] }'
```

The `whsec_…` secret returned at creation goes in `DIAL_WEBHOOK_SECRET`. See the [Webhooks reference](/documentation/platform/webhooks) for retries, signature format, and event types.

## What the adapter carries

| Direction | Channel              | Text              | Media                            |
| --------- | -------------------- | ----------------- | -------------------------------- |
| Inbound   | SMS                  | ✅                 | —                                |
| Inbound   | MMS                  | ✅                 | ✅ (image / video / audio / file) |
| Inbound   | iMessage             | ✅                 | ✅                                |
| Inbound   | Voice call           | ✅ (as transcript) | —                                |
| Outbound  | SMS / MMS / iMessage | ✅                 | ✅ (via attachment URLs)          |

Voice calls surface to the bot as their transcript, delivered via the `call.transcribed` event. Very short calls that never generate a transcript don't fire an `onNewMention`.

## Threads

A Chat SDK thread here is a **pair of phone numbers** — your Dial-owned number and the peer's — encoded as:

```
dial:{yourDialNumber}:{peerNumber}
```

Every distinct pair is a distinct thread. Chat SDK's per-thread state (conversation history, subscriptions, locks) is scoped per-pair, so multiple concurrent conversations don't leak into each other.

## Design notes

* **Outbound sends and transcript fetches** go through the official [`@getdial/sdk`](https://www.npmjs.com/package/@getdial/sdk) Node SDK — no hand-rolled HTTP.
* **Signature verification** uses Node's built-in `crypto.createHmac` + `timingSafeEqual`, matching the exact primitive Dial's server signs with.
* **ESM-only, TypeScript-first.** The adapter ships `dist/index.js` + `dist/index.d.ts` — no source, no tests, no `node_modules`.
* Peer-depends on `chat ^4.20.0` — install `chat` alongside this adapter.

## Learn more

* Chat SDK docs: [chat-sdk.dev](https://chat-sdk.dev)
* Adapter source + issues: [`GetDial-AI/chat-sdk-adapter`](https://github.com/GetDial-AI/chat-sdk-adapter)
* Node SDK the adapter wraps: [`@getdial/sdk`](/documentation/sdks/node)
* Related Dial concepts: [Webhooks](/documentation/platform/webhooks), [Events overview](/api-reference/events/overview)