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

# Voice calls

> Start phone calls from an owned number and register browser voice calls.

## Start an outbound call

```http theme={null}
POST /api/calls/make-outbound
Authorization: Bearer dh_your_api_key
Content-Type: application/json

{
  "from_number": "+15551110000",
  "to_number": "+15552220000",
  "variables": [{ "var_name": "patient_name", "var_value": "Alex" }]
}
```

```json theme={null}
{
  "err": false,
  "msg": "Call initiated successfully",
  "results": { "call_id": "3c2b1a09-8f7e-4d6c-9b8a-7d6c5b4a3f2e" }
}
```

`from_number` must be a number your organization owns, and the caregiver
attached to it runs the call. A number your organization does not own answers
`400` with `From number not found.` Use the returned `call_id` to
[read the result](https://docs.drivehealth.ai/results.md#calls) rather than guessing which row in the list is
yours.

## Register a web call

```http theme={null}
POST /api/calls/web/register
Authorization: Bearer dh_your_api_key
Content-Type: application/json

{ "assistant_id": "1a2b3c4d-...", "variables": [] }
```

```json theme={null}
{
  "err": false,
  "msg": "Success",
  "results": {
    "call_id": "3c2b1a09-8f7e-4d6c-9b8a-7d6c5b4a3f2e",
    "token": "eyJEXAMPLE.LIVEKIT.TOKEN"
  }
}
```

The response is `201 Created`. The token joins the LiveKit room for that call
and nothing else. A caregiver that is not in your organization answers `412`
with `AI Assistant not found.`

Complete `curl` example:

```bash theme={null}
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer $DRIVEHEALTH_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{\"assistant_id\":\"$DRIVEHEALTH_CAREGIVER_ID\",\"variables\":[]}" \
  "$DRIVEHEALTH_API_BASE/api/calls/web/register"
```

Register web calls from your backend. Return the short-lived LiveKit token and
`call_id` to the browser, but never return the organization API key. The
browser uses that token with the LiveKit server URL supplied for its
environment.

```js theme={null}
export async function registerWebCall({ apiBase, apiKey, caregiverId, variables = [] }) {
  const response = await fetch(`${apiBase}/api/calls/web/register`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ assistant_id: caregiverId, variables }),
  });

  const body = await response.json();
  if (!response.ok) throw new Error(body.msg || 'Could not register web call');

  return {
    callId: body.results.call_id,
    liveKitToken: body.results.token,
  };
}
```

## Call preconditions

Both routes run the same preconditions: workspace balance, caregiver readiness,
and the concurrency limit. A call over the limit is refused with
`{"err": true, "msg": "Maximum concurrency limit reached."}` and creates no
record.


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