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

# Tools & Live Call Updates

> Let a Voice agent call your API during a call, and push new information into a call that is still in progress.

## Overview

A Voice agent does not have to answer only from what it already knows. You can give it **tools** — endpoints on your own API that the agent calls in the middle of a conversation, while the caller is still on the line.

Some answers aren't ready that fast. A booking that needs a human to confirm, a quote that takes a minute to price — the agent can't hold the line while you finish. For those, you send the result to Reach once you have it, and the agent picks it up mid-call.

<CardGroup cols={2}>
  <Card title="Tools" icon="wrench">
    The agent calls your API and waits for the answer.
  </Card>

  <Card title="Live call updates" icon="right-to-bracket">
    You call Reach when the answer is ready.
  </Card>
</CardGroup>

## Tools

You configure tools in the Reach partner dashboard under **Agents → Tools**. Each tool is one endpoint on your API, described so the agent knows when to reach for it. Every tool has:

* **A name and description.** The agent reads the description to decide when to call the tool, so write it for the agent: what the tool does, and when it is the right thing to do.
* **A URL.** When the agent runs the tool, Reach sends an HTTP request here.
* **A credential.** Pick the credential Reach sends with the request and how to attach it. Credentials stay on your platform and are never visible to the agent.
* **Inputs.** Each input is one value Reach sends to your API every time the agent runs the tool. An input is either something the agent supplies from the conversation — "what day works for you?" — or something Reach fills in for you.

Tools are defined once for your platform and are available to every tenant's Voice agent. A tool can be set inactive to hide it from agents without deleting it.

<Note>
  The caller is on the line while your API is working. Answer quickly, and always answer with something. A timeout leaves the agent with nothing to say.
</Note>

### Work that can't finish during the call

When the work can't finish inside a phone call, split it in two:

<Steps>
  <Step title="Answer now">
    The agent calls your tool. You start the work and return immediately — a confirmation you've got it, plus anything the agent can usefully say right now.
  </Step>

  <Step title="Send the result when it lands">
    When the work finishes, push the result into the call with a live call update. If the call is still going, the agent uses it.
  </Step>
</Steps>

Don't have the agent poll a status tool in a loop. Each poll adds dead air the agent can't fill.

## The current call ID

Any tool input can be sourced from **Current Voice call ID** instead of being asked of the agent.

Reach then fills that input with the ID of the call the agent is on, and sends it on every request. Because Reach fills it in:

* It is always present, so the input can't be made optional.
* The agent can't invent it, get it wrong, or be talked into changing it. It comes from the call's signed session, not from the conversation.

The value is a Reach call ID — a UUID that identifies the call inside Reach:

```json theme={null}
{ "callId": "8f14e45f-ea6c-4c2b-9b21-b2c0f1f4b2a7" }
```

<Note>
  This is Reach's own identifier, not a phone-network or telephony call ID. You never need one of those — the Reach call ID is the only ID you use to address a call.
</Note>

Store it alongside whatever the tool call started; you'll need it to address the call later.

## Updating a call that's still in progress

`PATCH /partner/voice/active-calls/{callId}` pushes new information into a call that is still connected. Send a partner-scoped JWT plus the `reach-tenant-id` header naming the tenant whose call it is — either the Reach tenant UUID or your own external tenant ID.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://api.embedreach.com/partner/voice/active-calls/8f14e45f-ea6c-4c2b-9b21-b2c0f1f4b2a7 \
    -H "Authorization: Bearer YOUR_PARTNER_SCOPED_JWT" \
    -H "reach-tenant-id: tenant_123" \
    -H "Content-Type: application/json" \
    -d '{
      "context": {
        "booking_status": "confirmed",
        "booking_window": "Tuesday, 9am to 11am"
      }
    }'
  ```

  ```javascript Node.js theme={null}
  const callId = '8f14e45f-ea6c-4c2b-9b21-b2c0f1f4b2a7';

  const response = await fetch(
    `https://api.embedreach.com/partner/voice/active-calls/${callId}`,
    {
      method: 'PATCH',
      headers: {
        'Authorization': `Bearer ${process.env.REACH_PARTNER_JWT}`,
        'reach-tenant-id': 'tenant_123',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        context: {
          booking_status: 'confirmed',
          booking_window: 'Tuesday, 9am to 11am',
        },
      }),
    },
  );
  ```
</CodeGroup>

```json Response theme={null}
{
  "success": true,
  "message": "Success",
  "data": {
    "callId": "8f14e45f-ea6c-4c2b-9b21-b2c0f1f4b2a7",
    "status": "active",
    "appliedContextKeys": ["booking_status", "booking_window"],
    "filteredContextKeys": []
  }
}
```

### What you can send

`context` is a flat object of field names to string values.

| Limit                  | Value                                           |
| ---------------------- | ----------------------------------------------- |
| Fields per request     | 1 to 20                                         |
| Field name length      | 100 characters                                  |
| Value length           | 500 characters                                  |
| Whole `context` object | 8,192 bytes (field names plus values, as UTF-8) |

Values are always strings. Send `"true"`, not `true`; `"49.99"`, not `49.99`.

### Which fields are accepted

Reach applies only the fields configured for your platform. Everything else comes back in `filteredContextKeys` with a reason, and is dropped:

* `not_configured` — the field name isn't one of your platform's Voice fields, or it's a name Reach reserves.
* `invalid_value` — the field is configured, but the value doesn't fit its type. A **Boolean** field needs `"true"` or `"false"`, an **Enum** field needs one of its configured choices, and a **String** field can't be empty. Any value containing control characters or `{{` / `}}` is filtered too, whatever the field's type.

A request whose every field was filtered still returns `200`, so read `appliedContextKeys` to confirm what actually landed. Submitted values are never echoed back — the response names fields, never their contents.

Your platform's Voice fields are configured by Reach, so ask your Reach contact to add a field before you start sending it. Fields carrying a caller's name, phone, email, or address can't be pushed this way; Reach resolves those from the call itself.

<Warning>
  Never send secrets, credentials, or regulated data as call context. Field names whose words read like credentials — `auth_token`, `apiKey`, `password`, and the like — are never applied; they come back in `filteredContextKeys` as `not_configured`.
</Warning>

### When the agent uses it

Applied fields are available to the agent on its **next natural response**. An update does not interrupt the caller or make the agent speak.

Context is data, not direction. The agent is told to treat these values as facts it may use, never as instructions to follow, so you can't steer a call by writing a sentence into a field.

### Only while the call is live

<Warning>
  A call can only be updated while it is in progress. Once it has completed, disconnected, or failed, the update returns `409` and nothing is applied. If your answer arrives after the call ends, follow up through another channel.
</Warning>

### Retries

* Sending the same update twice is safe — the repeat applies the same values.
* Two different updates to the same field: the last one wins.
* Limits apply per platform (60 updates per minute) and per call (10 per minute). The tighter per-call limit stops a stuck integration from flooding one live conversation.

| Status | Meaning                                                                                                      |
| ------ | ------------------------------------------------------------------------------------------------------------ |
| `200`  | Applied. Read `appliedContextKeys` and `filteredContextKeys`.                                                |
| `400`  | The body, the `reach-tenant-id` header, or the call ID is malformed — including a call ID that isn't a UUID. |
| `404`  | No call with that ID for that tenant.                                                                        |
| `409`  | The call is no longer in progress.                                                                           |
| `424`  | The update was rejected. Retrying won't change the answer.                                                   |
| `429`  | Too many updates. Back off, then retry.                                                                      |
| `503`  | Temporary failure. Retry the same request as-is.                                                             |

<Note>
  `404` covers both a call ID that doesn't exist and one belonging to a different tenant. Reach answers both the same way, so a call ID can't be used to probe for calls you don't own.
</Note>

See [Update an active Voice call](/api-reference/endpoint/patch-partner-voice-active-calls-callid) for the full request and response schema.
