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

# Start Browser Call

> Let someone talk to an agent from a browser over WebRTC. No phone line needed.

Start a live voice call between a browser and an agent. The browser creates a WebRTC **offer** (an SDP string); you send it here and get back an **answer** that the browser uses to finish connecting. The response is **201 Created** with the call and the answer.

> [!WARNING]
>
> **Send the offer from your server, never from the browser.** The request needs your API key (scope `agents`). Have your frontend send the offer to your backend, call this endpoint from there, and return only the `answer` and the call `id` to the browser.

Costs: AI credits for speech, LLM and memory, like a phone call. No Dialer balance or phone line is used. The workspace must have an active plan.

## Request

### Headers

**Authorization** `string` *required*

Bearer token with your API key (scope `agents`)

**Content-Type** `string` *required*

application/json

### Path

**agentId** `string` *required*

The agent `id` (UUID)

### Body

**offer** `string` *required*

The finished SDP offer from the browser, 1 to 32,768 characters, starting with `v=0`. It must contain an audio track and a data channel, and ICE gathering must be complete (wait for `iceGatheringState === "complete"` before reading `localDescription`)

## Response

**201 Created**

```json theme={null}
{
  "success": true,
  "data": {
    "call": {
      "id": "6b1f0c52-4d7e-4b8a-9d3e-2a1c8e7f0a11",
      "agent_id": "9c2e1d7a-0b3f-4e6a-8f1d-5a7b9c0e2d44",
      "agent_name": "Front desk",
      "phone_line_id": null,
      "direction": "web",
      "to": "browser",
      "from": "browser",
      "status": "dialing",
      "duration_secs": null,
      "record": false,
      "source": "api"
    },
    "answer": "v=0\r\no=- ..."
  }
}
```

The call is trimmed above; see [Get call](https://docs.60db.ai/api-reference/agents/get-call.md) for every field. Pass `answer` to `RTCPeerConnection.setRemoteDescription({ type: "answer", sdp })`. The call moves to `in_progress` when the browser connects, and finishes with the usual `call.completed` event. Browser calls are not shown by polling the runtime, so use [Get call](https://docs.60db.ai/api-reference/agents/get-call.md) or the events.

## Limits

* Up to 3 browser calls at once per workspace.
* A call lasts up to 5 minutes.
* No call transfer and no keypad (DTMF). Audio is phone quality.

## Errors

| Status | Code | Meaning |
| - | - | - |
| 400 | `VALIDATION_ERROR` | `offer` is missing, too long, or not an SDP. `field` names it |
| 402 | `INSUFFICIENT_CREDITS` | Not enough AI credits to start a call |
| 403 | `POLICY_DENY` | No active plan, or the API key lacks the `agents` scope |
| 404 | `AGENT_NOT_FOUND` | No agent with this ID in your workspace |
| 429 | `TOO_MANY_WEB_CALLS` | 3 browser calls are already running. Wait for one to end |
| 503 | `WEB_CALLS_UNAVAILABLE` | Browser calls are not available right now. Retry shortly |
| 502 | `AGENTS_UPSTREAM_UNAVAILABLE` | The call runtime failed or did not answer |

## Example

```bash cURL theme={null}
curl -X POST https://api.60db.ai/agents/AGENT_ID/web-calls \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{ "offer": "v=0\r\no=- ..." }'
```

```javascript Browser + your server theme={null}
// Browser
const pc = new RTCPeerConnection();
const mic = await navigator.mediaDevices.getUserMedia({ audio: true });
mic.getAudioTracks().forEach((t) => pc.addTrack(t, mic));
pc.createDataChannel('control');
pc.ontrack = (e) => { audioEl.srcObject = e.streams[0]; };
await pc.setLocalDescription(await pc.createOffer());
await new Promise((resolve) => {
  if (pc.iceGatheringState === 'complete') return resolve();
  pc.onicegatheringstatechange = () => pc.iceGatheringState === 'complete' && resolve();
});
const res = await fetch('/my-backend/talk', { method: 'POST', body: JSON.stringify({ offer: pc.localDescription.sdp }) });
const { answer, callId } = await res.json();
await pc.setRemoteDescription({ type: 'answer', sdp: answer });

// Your server: forward the offer with your API key, return { answer, callId: data.call.id }
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.