# OpenAI Agents SDK

> Give an OpenAI Agents SDK agent your messaging channels, and let it answer incoming messages, in about ten minutes.

Source: https://docs.vlayers.ai/developers/frameworks/openai

Kind: Tutorial

## What you'll build

An OpenAI 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/openai`, which connects the Agents SDK to the VoiceLayer [channel tools](https://docs.vlayers.ai/developers/mcp) 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](https://docs.vlayers.ai/get-started/authentication). With a test key every send goes to a simulated carrier and costs nothing.
- An OpenAI API key in `OPENAI_API_KEY`.
- No phone number needed: with a test key, your workspace's free [test sender](https://docs.vlayers.ai/developers/test-mode#your-free-test-sender) stands in for one.

## Install

```bash
npm install @voicelayer/agents @openai/agents@0.3
```

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

```bash
export VOICELAYER_API_KEY=vl_test_...
export OPENAI_API_KEY=...
npm pkg set type=module
```

## Give your agent VoiceLayer's tools

Save this as `agent.ts`.

```ts title="agent.ts"
import { Agent, run } from '@openai/agents';
import { voicelayerMcpServer } from '@voicelayer/agents/openai';

await using voicelayer = await voicelayerMcpServer();
const agent = new Agent({ name: 'support', instructions: 'You answer customers in one short sentence.', mcpServers: [voicelayer] });

const result = await run(agent, 'Text "Hello from VoiceLayer" to +15550101 over sms.');
console.log(result.finalOutput);
```

- `voicelayerMcpServer()` connects the Agents SDK to VoiceLayer and returns an MCP server for `mcpServers`. `await using` closes the connection when the file ends.
- `run()` asks the agent to text `+15550101`, a reserved test number. The agent calls `message_send`.

Run it:

```bash
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`.

```ts title="serve.ts"
import { Agent } from '@openai/agents';
import { serveDirect } from '@voicelayer/agents/openai';

const agent = new Agent({ name: 'support', 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 run, and the reply is sent back in that conversation. It keeps running until you stop the process.

```bash
npx --yes tsx serve.ts
```

```text
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`.

```ts title="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}`);
```

```bash
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](https://docs.vlayers.ai/developers/handoff) 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`](https://docs.vlayers.ai/errors/unauthorized) | 401 | Send `Authorization: Bearer <key>` with a secret key from the dashboard (Developers → API keys). Test keys start with `vl_test_`. |
| [`insufficient_scope`](https://docs.vlayers.ai/errors/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`](https://docs.vlayers.ai/errors/test_address_required) | 422 | Use a reserved test address, or a live key to reach a real person. |
| [`test_mode_only`](https://docs.vlayers.ai/errors/test_mode_only) | 403 | Use a test key (`vl_test_…`) from the dashboard. |
| [`test_limit_reached`](https://docs.vlayers.ai/errors/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](https://docs.vlayers.ai/developers/handoff).

## Next steps

- [The MCP tools](https://docs.vlayers.ai/developers/mcp): what each tool does and which scope it needs.
- [Test mode](https://docs.vlayers.ai/developers/test-mode): more ways to simulate people.
- [Python frameworks](https://docs.vlayers.ai/developers/frameworks/python): the same setup in Python.
