---
title: "Talk Links"
description: "Create a single-use link that opens a browser page where a person talks to an agent's current draft, the same conversation as the dashboard's Talk button."
---

A talk link is a URL you can hand to a person: they open it, click **Start
talking**, and talk to the agent's current draft from their browser. They
need no ThunderPhone account. It is the same conversation as the dashboard's
**Talk** button and the [mic session](/api-reference/mic-sessions) endpoint:
the draft configuration, recorded and billed like any test call.

Coding agents use it to ask someone to try a change: edit the draft, create
a link, and send it with a note about what to listen for.

Each link works once and expires after 30 minutes unless you set another
expiry (at most 120 minutes). The page shows only the agent's name.

Creating a link is part of the agent iteration toolkit, which is enabled per
organization while it is in beta. A link already created keeps working
either way.

## Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `POST` | `/v1/agents/{agent_id}/talk-links` | API key | Create a talk link |
| `GET` | `/v1/talk-links/{token}` | none | Check a link before connecting |
| `POST` | `/v1/talk-links/{token}/session` | none | Start the conversation (works once) |

The two `{token}` endpoints are what the talk page calls. You only need them
to build your own page; the token in the URL is their credential.

## Create a talk link

<CodeGroup>
```bash cURL
curl -X POST https://api.thunderphone.com/v1/agents/12/talk-links \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"expires_in_minutes": 30, "note": "shorter greeting"}'
```

```python Python
link = requests.post(
    "https://api.thunderphone.com/v1/agents/12/talk-links",
    headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
    json={"expires_in_minutes": 30, "note": "shorter greeting"},
).json()
print(link["url"])
```

```javascript Node.js
const link = await fetch("https://api.thunderphone.com/v1/agents/12/talk-links", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.THUNDERPHONE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ expires_in_minutes: 30, note: "shorter greeting" }),
}).then((r) => r.json());
```
</CodeGroup>

### Request fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `variant_id` | integer | no | Talk to one [A/B variant](/api-reference/split-testing) of the draft instead of the plain draft |
| `language` | string | no | Language code for the conversation, such as `es`. Defaults to the agent's primary language |
| `expires_in_minutes` | integer | no | Minutes until an unused link stops working. Default `30`, between `1` and `120` |
| `note` | string | no | Your own label for the link, up to 500 characters. Never shown on the talk page |

### Response

`201 Created`

```json
{
  "url": "https://app.thunderphone.com/talk/Vh3k…",
  "expires_at": "2026-10-19T18:30:00Z"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `url` | string | The page to send. It contains the link's token; treat it like a password until it is used |
| `expires_at` | string | When an unused link stops working (ISO 8601) |

ThunderPhone stores only a hash of the token, so a lost URL cannot be shown
again. Create another link instead.

### Errors

| Status | Condition |
|--------|-----------|
| `400` | `expires_in_minutes` outside 1–120, an unknown `language`, or a `variant_id` that is not one of this agent's variants |
| `403` | `feature_not_enabled`: the organization does not have the agent iteration toolkit, which is enabled per organization while it is in beta. The body's `next_step` says what to do |
| `404` | Agent not found in this organization |

## Check a link

`GET /v1/talk-links/{token}` needs no authentication. It returns what the
talk page shows before the person connects, and nothing else about your
organization.

```json
{
  "agent_name": "Front Desk",
  "expires_at": "2026-10-19T18:30:00Z",
  "used": false,
  "recorded": true
}
```

`recorded` says whether the conversation will be recorded, from the agent
draft's recording setting (the session runs the draft).

`404` with `code: "talk_link_not_found"` means the token is unknown or the
link's agent has moved to another organization.

## Start the conversation

`POST /v1/talk-links/{token}/session` needs no authentication and no body.
The first successful request uses the link and starts a browser session with
the agent's draft at that moment, exactly like
[`POST /v1/mic-session`](/api-reference/mic-sessions):

```json
{
  "call_id": 987654321,
  "token": "<livekit-jwt>",
  "room_name": "mic-987654321",
  "server_url": "wss://livekit.thunderphone.com",
  "agent_name": "Front Desk"
}
```

Join the room with the LiveKit Web SDK as described under
[Connecting from the browser](/api-reference/mic-sessions#connecting-from-the-browser).
The conversation appears in [call history](/api-reference/calls) with
`direction="test"`.

### Errors

| Status | Condition |
|--------|-----------|
| `400` | The link's `language` can no longer be used with the agent's current voice. The link stays unused |
| `402` | The organization's balance or test-call budget cannot cover a call. The link stays unused |
| `404` | `talk_link_not_found`: unknown token, or the link's agent has moved to another organization |
| `410` | `talk_link_used`: the link was already used. `talk_link_expired`: it expired before use, or its agent was moved to another organization and back |
| `422` | The agent's voice cannot be used, for example a custom voice that was deleted. The link stays unused |
| `429` | Too many requests for this link or from this address, or the organization is at its [concurrent-call limit](/api-reference/introduction#call-limits). The link stays unused |
| `503` | Voice infrastructure unavailable. The link stays unused |

<Note>
  A talk-link conversation is billed to the organization that created the
  link, exactly like a Talk test call, and is recorded according to the
  agent's recording setting. Anyone holding an unused link can start one
  conversation, so send it only to the person who should talk to the agent.
</Note>

## MCP

The [ThunderPhone MCP server](/guides/thunderphone-mcp-server) exposes this as
`create_talk_link`, so a coding agent can create the link and hand you the URL.

---

## Related

<CardGroup cols={2}>
  <Card title="Mic Sessions" icon="microphone" href="/api-reference/mic-sessions">
    Start the same session from your own page with an API key.
  </Card>
  <Card title="Agents" icon="robot" href="/api-reference/agents">
    Edit the draft the link talks to.
  </Card>
</CardGroup>
