> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stateset.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build Your First Voice Agent

> From a tenant API key to a live call with a custom tool, end to end.

# Build Your First Voice Agent

The end-to-end path: mint a key, configure a tenant, create an agent with a custom tool, attach a
phone number, register a webhook, place a test call, and watch it live.

## Prerequisites

* A running voice server — see [Overview](/stateset-voice/overview#getting-started) for the
  Docker Compose stack
* Twilio credentials and a phone number
* An OpenAI key (and an ElevenLabs key if you want its TTS)

## 1. Mint a tenant API key

Tenant credentials are the unit of access. Every subsequent call uses one as a Bearer token.

<Warning>
  The key is returned **once**. Store it before moving on — it can be revoked and reissued, not
  retrieved.
</Warning>

## 2. Configure the tenant account

Set the tenant's provider credentials and defaults — audio output provider, VAD tuning, and
handoff workflow. See [Configuration](/stateset-voice/configuration).

## 3. Create the agent

An agent version carries its prompt, model settings, and `tools[]`. Each tool is a JSON-schema
function plus **your** HTTPS endpoint.

### How your tool endpoint is called

Mid-call, the server POSTs to your endpoint with:

* `Authorization` passthrough
* An `X-Webhook-Signature` HMAC over the raw body — the **same** scheme as webhooks

Your JSON reply becomes the tool result the model sees.

<Tip>
  Verify that signature with the same helper you use for webhooks. One implementation covers both
  — see [Verifying signatures](/stateset-voice/webhooks#verifying-signatures).
</Tip>

## 4. Attach a phone number

Route a number to the agent with `direction` and `agent_id`. Inbound calls to that number now
reach this agent.

## 5. Register a webhook for call results

Point an endpoint at the events you care about. For a first agent,
`voice.call.started`, `voice.call.ended`, and `voice.session.completed` are enough — the last one
carries persisted transcripts.

See the full [event catalog](/stateset-voice/webhooks#event-catalog).

## 6. Place a test call

Either call the number, or place an outbound call with `POST /make-call`.

<Tip>
  Faster still: `POST /voice/web-calls` gives you a browser call with **no phone number needed**,
  on the same pipeline. It's the tightest loop while iterating on a prompt.
</Tip>

Send an `Idempotency-Key` on `make-call` — 24-hour replay protection means a retried request
won't place a second real call.

## 7. Inspect the session afterwards

Session records and transcripts are persisted. `voice.session.completed` delivers them to your
webhook, and the session APIs let you fetch them directly.

## 8. Watch a live call

| Route                                | Use                                              |
| ------------------------------------ | ------------------------------------------------ |
| `GET /sessions`                      | Enumerate active sessions                        |
| `GET /logs` *(WebSocket)*            | Real-time log broadcast                          |
| `POST /sessions/:id/actions/:action` | `monitor`, `whisper`, `barge`, `escalate`, `end` |

`monitor` listens; `whisper` speaks to the agent only; `barge` joins the caller; `escalate` hands
to a human; `end` terminates.

## Troubleshooting

| Symptom                            | Check                                                                                                          |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| Twilio webhooks rejected           | Signature validation — credentials must match the account sending them                                         |
| Media stream fails to authenticate | `STREAM_AUTH_SECRET` is set, or legacy transport is explicitly enabled. Production **fails fast** with neither |
| No audio from the agent            | `GET /admin/tenants/audio-providers` — compare configured vs effective provider and the fallback reason        |
| Agent talks over the caller        | Per-tenant VAD threshold and silence padding                                                                   |
| First audio feels slow             | Try `turn_response_mode: streaming` for that tenant                                                            |

## Next

<CardGroup cols={2}>
  <Card title="SDKs" icon="cube" href="/stateset-voice/sdks">
    TypeScript, Python, browser calls.
  </Card>

  <Card title="Configuration" icon="gear" href="/stateset-voice/configuration">
    Auth transport, providers, VAD, readiness.
  </Card>

  <Card title="Webhooks" icon="bell" href="/stateset-voice/webhooks">
    Events and signature verification.
  </Card>

  <Card title="MCP server" icon="plug" href="/stateset-voice/mcp-server">
    Build and tune agents from Claude.
  </Card>
</CardGroup>
