> 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 AI SDK integration

> Give any AI SDK model Dial tools for sending SMS and placing AI voice calls.

`@getdial/ai-sdk` packages Dial's operations as [Vercel AI SDK](https://ai-sdk.dev)
tools, so a model can send messages and place calls as part of its reasoning. It's
the TypeScript analog of the [LangChain integration](/documentation/sdks/langchain):
a thin adapter over [`@getdial/sdk`](/documentation/sdks/node) — it adds nothing to
the REST contract, it just shapes Dial's operations into AI SDK `tool()` definitions.

**Not Vercel-only.** The "Vercel AI SDK" is the open-source `ai` package — it runs
on any JS host (Next.js on any platform, Node, Hono, Cloudflare Workers, Deno, Bun)
and with any tool-calling model (Claude, GPT, Gemini, …). These tools work wherever
it does.

## Install

```bash
npm install @getdial/ai-sdk ai zod
```

`ai` and `zod` are peer dependencies — your app already has them. The package pulls
in `@getdial/sdk` as its Dial client.

## Give the tools to a model

The quickest way is `dialTools` — construct it once with your API key and spread it
into the `tools` field. To switch models, change one line; the tools don't change.

```typescript
import { streamText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { dialTools } from "@getdial/ai-sdk";

const result = streamText({
  model: anthropic("claude-opus-4-8"), // or openai("gpt-5"), google("gemini-2.5-pro"), …
  tools: dialTools({
    apiKey: process.env.DIAL_API_KEY!,
    fromNumberId: process.env.DIAL_FROM_NUMBER_ID, // optional default "from"
  }),
  messages,
});
```

When `fromNumberId` is set, it's the default for `sendMessage` and `makeCall` — the
model can omit it, mirroring the CLI's saved default `from_number_id`.

## Available tools

| Tool                  | Action                                                                  |
| --------------------- | ----------------------------------------------------------------------- |
| `listNumbers`         | List your phone numbers                                                 |
| `purchaseNumber`      | Provision a new number (billable)                                       |
| `setNumberProperties` | Update a number's nickname / inbound instruction / voice / duration cap |
| `sendMessage`         | Send an SMS (optionally MMS)                                            |
| `listMessages`        | List recent messages                                                    |
| `makeCall`            | Place an AI voice call                                                  |
| `listCalls`           | List recent calls                                                       |
| `getCall`             | Fetch one call by id                                                    |
| `getBilling`          | Wallet balance, subscription, per-number mode                           |
| `waitForMessage`      | Block until the next inbound SMS arrives, or time out                   |

Each `execute` returns the structured Dial object (a `Message`, `Call`, etc.) — the
AI SDK serializes the tool result back into the model for you.

### Or pick individual tools

When you only want a subset, import the factories directly:

```typescript
import { sendMessageTool, makeCallTool } from "@getdial/ai-sdk";

const tools = {
  sendMessage: sendMessageTool({ apiKey: process.env.DIAL_API_KEY!, fromNumberId: "pn_..." }),
  makeCall: makeCallTool({ apiKey: process.env.DIAL_API_KEY!, fromNumberId: "pn_..." }),
};
```

## Example: a Next.js chat route

A typical AI SDK app exposes a route handler that streams the model's response. Drop
`dialTools(...)` into it and the chat can text and call people:

```typescript title="app/api/chat/route.ts"
import { streamText, convertToModelMessages, type UIMessage } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { dialTools } from "@getdial/ai-sdk";

export async function POST(req: Request) {
  const { messages }: { messages: UIMessage[] } = await req.json();

  const result = streamText({
    model: anthropic("claude-opus-4-8"),
    tools: dialTools({
      apiKey: process.env.DIAL_API_KEY!,
      fromNumberId: process.env.DIAL_FROM_NUMBER_ID,
    }),
    messages: convertToModelMessages(messages),
  });

  return result.toUIMessageStreamResponse();
}
```

Now an end user typing *"text the plumber at +14155550123 that I'll be 10 minutes
late"* gets the model to call `sendMessage` with the right `to` and `body`.

## Send a code and await the reply

`waitForMessage` pairs with `sendMessage` so the model can verify a number inside a
single turn — send a one-time code, then block on the inbound reply:

```typescript
import { generateText } from "ai";
import { dialTools } from "@getdial/ai-sdk";

const { text } = await generateText({
  model,
  tools: dialTools({ apiKey: process.env.DIAL_API_KEY!, fromNumberId: "pn_..." }),
  // The model calls sendMessage to send the code, then waitForMessage to read the reply.
  prompt: "Text +14155550123 the verification code 4821, wait up to 60s for their reply, and tell me whether it matches.",
});
```

`waitForMessage` opens the account [event stream](/documentation/platform/stream-account-events),
returns the first matching `message.received`, and times out after `timeoutSeconds`
(default 30).

`sendMessage` is a write action and **isn't idempotent** — a re-invoke after a
failure can send a duplicate. `makeCall` accepts an `idempotencyKey`: a re-invoke
with the same key returns the already-placed call instead of dialing again. See
[Retries and idempotency](/documentation/reference/errors#retries-and-idempotency).

## Receiving events beyond a single turn

`waitForMessage` is a one-shot wait, backed by a **presence-based** stream (not
at-least-once — missed events replay only on reconnect within \~2 minutes). To react
to inbound SMS or completed calls durably and continuously, don't model it as a tool:
use the [Node SDK's](/documentation/sdks/node) `newEventsConnection()` directly, or
register a [webhook](/documentation/platform/webhooks) (signed and retried
at-least-once), and feed events into your agent however suits your app.