Tutorial

Vercel AI SDK

Give a Vercel AI SDK agent your messaging channels, and let it answer incoming messages, in about ten minutes.

What you'll build

An AI SDK agent that texts a test number with VoiceLayer's message_send tool, then answers a simulated incoming text from your own process. You use @voicelayer/agents/ai-sdk, which loads the VoiceLayer channel tools as AI SDK tools over MCP.

Prerequisites

  • Node.js 22 or later, and TypeScript 5.2 or later if you compile your own (the examples run with tsx). The examples are ES modules with top-level await, and use await using, which closes a connection when the file ends (TypeScript 5.2 or later with esnext.disposable in lib; or close it yourself with await tools[Symbol.asyncDispose]()).
  • A test key in VOICELAYER_API_KEY (vl_test_…): see Authentication and API keys. With a test key every send goes to a simulated carrier and costs nothing.
  • A model provider for the AI SDK. The quickstart uses the AI Gateway model openai/gpt-4.1-mini, so set AI_GATEWAY_API_KEY.
  • No phone number needed: with a test key, your workspace's free test sender stands in for one.

Install

npm install @voicelayer/agents ai@7 @ai-sdk/mcp@2

Set the keys once. VoiceLayer's helpers read VOICELAYER_API_KEY themselves, so no example passes a key:

export VOICELAYER_API_KEY=vl_test_...
export AI_GATEWAY_API_KEY=...
npm pkg set type=module

Give your agent VoiceLayer's tools

Save this as agent.ts.

agent.ts
import { ToolLoopAgent } from 'ai';
import { voicelayerTools } from '@voicelayer/agents/ai-sdk';

await using tools = await voicelayerTools();
const agent = new ToolLoopAgent({ model: 'openai/gpt-4.1-mini', instructions: 'You answer customers in one short sentence.', tools });

const { text } = await agent.generate({ prompt: 'Text "Hello from VoiceLayer" to +15550101 over sms.' });
console.log(text);
  • voicelayerTools() connects to VoiceLayer over MCP and returns the tools as AI SDK tools. await using closes the connection when the file ends.
  • agent.generate() asks the agent to text +15550101, a reserved test number. The agent calls message_send.

Run it:

npx --yes tsx agent.ts

The agent texts the test number and prints its answer.

Answer incoming messages

Direct mode: your process answers the conversations VoiceLayer hands to code. Save this as serve.ts.

serve.ts
import { ToolLoopAgent } from 'ai';
import { serveDirect } from '@voicelayer/agents/ai-sdk';

const agent = new ToolLoopAgent({ model: 'openai/gpt-4.1-mini', instructions: 'You answer customers in one short sentence.' });
await serveDirect(agent);

serveDirect(agent) answers incoming messages with the agent: each message in a conversation your code controls becomes one generation, and the reply is sent back in that conversation. It keeps running until you stop the process.

npx --yes tsx serve.ts
voicelayer: answering incoming messages (Ctrl+C to stop)

Test it without a person

With the server from the last step running, open a second terminal and simulate a person texting you. Save this as simulate.ts.

simulate.ts
import { simulateInbound } from '@voicelayer/agents';

const { conversationId } = await simulateInbound({ text: 'What are your opening hours?' });
console.log(`Simulated a text from +15550101 in conversation ${conversationId}`);
npx --yes tsx simulate.ts

The first terminal prints voicelayer: replied in conversation …. Open Conversations in the dashboard and turn on test conversations to see the text and the reply. Both went to the simulated carrier.

Direct mode answers conversations your code controls (api). A new workspace's free test sender is yours by default; if the number you use has an agent that answers texts, hand the conversation to your code first.

To answer real people, swap the test key for a live key. Nothing else changes.

Handle errors

A missing key stops the program at start with a message that names VOICELAYER_API_KEY. A wrong key, or anything else the API refuses, comes back as an error with a stable code:

ErrorStatusHow to fix
unauthorized401Send Authorization: Bearer <key> with a secret key from the dashboard (Developers → API keys). Test keys start with vl_test_.
insufficient_scope403Use a key with the scope named in details.required, or one whose restriction covers this channel or Binding.
test_address_required422Use a reserved test address, or a live key to reach a real person.
test_mode_only403Use a test key (vl_test_…) from the dashboard.
test_limit_reached429Try again after midnight UTC, or ask for a higher test limit.

If the first terminal never prints a reply, the conversation is answered by your workspace's agent, not by your code. See Hand off a conversation.

Next steps

On this page