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-levelawait, and useawait using, which closes a connection when the file ends (TypeScript 5.2 or later withesnext.disposableinlib; or close it yourself withawait 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 setAI_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@2Set 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=moduleGive your agent VoiceLayer's tools
Save this as 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 usingcloses the connection when the file ends.agent.generate()asks the agent to text+15550101, a reserved test number. The agent callsmessage_send.
Run it:
npx --yes tsx agent.tsThe 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.
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.tsvoicelayer: 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.
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.tsThe 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:
| Error | Status | How to fix |
|---|---|---|
unauthorized | 401 | Send Authorization: Bearer <key> with a secret key from the dashboard (Developers → API keys). Test keys start with vl_test_. |
insufficient_scope | 403 | Use a key with the scope named in details.required, or one whose restriction covers this channel or Binding. |
test_address_required | 422 | Use a reserved test address, or a live key to reach a real person. |
test_mode_only | 403 | Use a test key (vl_test_…) from the dashboard. |
test_limit_reached | 429 | Try 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
- The MCP tools: what each tool does and which scope it needs.
- Test mode: more ways to simulate people.
- LangChain and LangGraph: the same setup with LangGraph.