> For the complete documentation index, see [llms.txt](https://docs.concurrence.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.concurrence.com/developer-guide/guides/live-call-context.md).

# Set Context on a Live Voice Call

Set verified context on a live voice call from a trusted backend so context-bound tools receive the value server-side without the model seeing it.

Use this endpoint when a trusted backend learns a verified value, such as a patient ID, only after a voice call has started. The backend sets the value on the live call, and any tool bound to that context key receives it server-side when it runs. The value never enters the model's prompt, so the agent does not hear, say, or choose it.

A common pattern is a verification step that runs before the call reaches the agent. Instead of reading the verified ID aloud in a handoff briefing, the backend sends it to Concurrence over an authenticated API call.

This guide extends [Pass Context to Tool Parameters](/developer-guide/guides/conversation-context.md), which covers start-time context, `accepted_context`, and Context Graph bindings. Read that guide first.

{% hint style="warning" %}
**Your backend owns correlation.** The platform checks that the call belongs to your workspace and that the key is declared. It cannot check that the value belongs to the person on this call. Binding one person's ID to another person's call is the main risk in this integration, and only the sender can prevent it. Follow [Find the Call](#find-the-call) exactly.
{% endhint %}

## Before You Start

You need:

* A voice service whose `accepted_context` declares the key. See [Declare the Key](#declare-the-key).
* A Context Graph whose tool binding names the key through `context_bindings` or `context_binding_aliases`, as in [Bind a Parameter in the Context Graph](https://docs.concurrence.com/developer-guide/guides/pages/2aLSJdlgnky98MXI6u5M#id-2.-bind-a-parameter-in-the-context-graph).
* A workspace API key whose role holds `Conversation:CreateForPrincipal`. Admin and owner roles hold it, and a key narrowed to only that permission works. A key without it gets `403`. Keep the key in your backend's secret store; never put it on a device, in a browser, or in an agent's prompt. Prefer a dedicated key with a short expiry. See [Authentication & API Keys](/developer-guide/platform-api/platform-api/authentication.md).
* The agent engine host for your region. The endpoint is served by the agent engine, not the Platform API host: `https://agent-engine.api-us.concurrence.com` in US production, `https://agent-engine.api-staging.concurrence.com` in staging. See [Regions & Endpoints](/developer-guide/getting-started/regions-and-endpoints.md#service-specific-hosts).

This operation is not published in the Platform OpenAPI document, so this page describes its contract directly.

## Declare the Key

Add the key to the service's `accepted_context`:

```json
{
  "key": "patient-id",
  "type": "string",
  "required": false,
  "description": "Verified patient ID set by the backend after verification"
}
```

* Use `required: false`. The value arrives after the call starts, so nothing supplies it at call start.
* Use `string` for identifiers, even numeric-looking ones, so leading zeros survive. A `number` key accepts only a JSON number.
* `channel-id` is reserved and cannot be set through this endpoint.
* A service update replaces the whole `accepted_context` list. Keep every key you still need; see [Declare the Schema on the Service](https://docs.concurrence.com/developer-guide/guides/pages/2aLSJdlgnky98MXI6u5M#id-1.-declare-the-schema-on-the-service).

An undeclared key returns `422`. Keep the tool binding on the tool that calls your API, not on a companion skill's own inputs; see [How the Agent Uses the Value](#how-the-agent-uses-the-value).

## Find the Call

The endpoint needs the call's `call_sid`. Poll the active-sessions list after you place or receive the call:

{% openapi src="<https://platform.api-us.concurrence.com/v1/openapi.json>" path="/v1/{workspace\_id}/sessions/active" method="get" %}
<https://platform.api-us.concurrence.com/v1/openapi.json>
{% endopenapi %}

The list is best-effort and real-time. A new call usually appears within a few seconds, but it can appear late. Take a `call_sid` only when **exactly one** entry matches all of these:

* `service_id` is the service you expect.
* `caller_id` is the number the call came from.
* The session started at or after the time you placed or transferred the call. Derive the start from `duration_seconds` and the time you read the list.
* No other call of yours has already claimed that `call_sid`.

If more than one entry matches, or none does, do not send the value. The agent then handles the call without it, under whatever policy your Context Graph defines for unverified callers. When your calls share an origin number, for example a fixed trunk number, concurrent calls share a `caller_id`, so this rule matters most.

## Set the Value

Send the value as soon as you have the `call_sid`, before the agent needs it:

```http
POST https://agent-engine.api-us.concurrence.com/agent/calls/{call_sid}/context
Authorization: Bearer <workspace API key>
Content-Type: application/json

{"context": {"patient-id": "TEST-PATIENT-0001"}}
```

| Part       | Rule                                                                                                                                               |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `call_sid` | The value from the active-sessions entry. Up to 64 characters of letters, digits, `_`, and `-`.                                                    |
| `context`  | An object with 1 to 50 keys.                                                                                                                       |
| Key        | A key declared in the service's `accepted_context`: 2 to 63 lowercase letters, digits, and internal hyphens.                                       |
| Value      | A JSON string of 1 to 1,024 characters, or a JSON number. The type must match the declaration. Booleans, arrays, objects, and `null` are rejected. |

Put the value only in the request body. Never put it in the URL path or query string.

### Set-Once Semantics

Each key can be set once per call:

* Sending the same value again returns `200` with the key reported as `unchanged`.
* Sending a different value for a key that is already set returns `409`. Nothing is overwritten, and the original value stays.
* A request with several keys is applied together. Keys that conflict are reported, and the others are still bound.

Because of this, resending the same body after a timeout or `503` is safe. Outbound voice calls created with a start-time `context` map hold those keys from the start, so setting one of them to a different value also returns `409`.

## Responses

A successful request returns:

```json
{
  "call_sid": "2c725f98-a9c6-4be2-8a48-6eb42d1f81cb",
  "keys": { "patient-id": "bound" }
}
```

Error bodies name keys, never values.

| Status | Body                                                                                                                                                                                                                                                                   | What to do                                                                                                                                                                                                                                                              |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`  | Each key is `bound`, or `unchanged` on a repeat with the same value                                                                                                                                                                                                    | Done.                                                                                                                                                                                                                                                                   |
| `401`  | Missing, invalid, or expired credential                                                                                                                                                                                                                                | Fix the credential. Do not retry with the same one.                                                                                                                                                                                                                     |
| `403`  | `"Setting call context requires Conversation:CreateForPrincipal"`                                                                                                                                                                                                      | Use a key whose role holds that permission. Do not retry.                                                                                                                                                                                                               |
| `404`  | `"Call not found"`                                                                                                                                                                                                                                                     | The call is unknown, has ended, or belongs to another workspace; all three look the same. A call whose conference has ended also answers `404` even while its session is still tearing down, so set the value before the call ends. Check the `call_sid`. Do not retry. |
| `409`  | `message`, `conflicting_keys`, and the per-key outcomes in `keys`                                                                                                                                                                                                      | A different value is already set for this call. Treat it as a correlation bug: do not retry, and alert.                                                                                                                                                                 |
| `422`  | `message` of `"Undeclared context keys"`, `"Reserved context keys"`, or `"Context value type mismatch"`, with the affected `keys`; or, for a malformed body, a validation error whose `detail` entries carry only `loc`, `msg`, and `type`, never the submitted values | A configuration or client bug. Fix the declaration or the request. Do not retry.                                                                                                                                                                                        |
| `503`  | `"Call context store unavailable"` or `"Service config unavailable"`                                                                                                                                                                                                   | Nothing was written. Retry the same body with exponential backoff, for about 10 seconds in total.                                                                                                                                                                       |

## How the Agent Uses the Value

Your backend sets each key once per call, after it has verified the caller and found the call.

<figure><img src="https://1651797362-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2YwM0LHtEGLt9S21OyC3%2Fuploads%2Fgit-blob-880ee733fb9fddc3f5044271559d50788e60fe9b%2Flive-call-context-flow.svg?alt=media" alt="A tool bound to a declared key is refused until your backend sets the key on the live call with POST /agent/calls/{call_sid}/context. Once the key is set, the tool runs with the value injected, and the model never sees it."><figcaption><p>Each key is set once per call. Error bodies name keys, never values.</p></figcaption></figure>

* **Injected at dispatch.** A tool whose binding names the key receives the value when it runs. The model does not supply it and cannot override it. A value the call already started with takes precedence.
* **Fails closed until set.** On voice calls, a binding to a declared key that the call did not start with refuses to run until the key is set. The tool returns a rejected-input result instead of calling your system without the value. This applies to `context_bindings` lists as well as aliases, and on voice it takes precedence over the missing-optional-key behavior in [Pass Context to Tool Parameters](https://docs.concurrence.com/developer-guide/guides/pages/2aLSJdlgnky98MXI6u5M#id-3.-supply-values-when-creating-the-conversation). Text conversations follow that page.
* **Retry after the key lands.** If the tool runs before your request arrives, it is refused, and the agent can call it again on a later turn. On some voice runtimes, the agent also receives a short note naming the key (never the value) when it is set, without interrupting the caller.
* **Store failures fail closed.** If the stored value cannot be read, the tool returns a generic error instead of running without it.
* **Not on companion skill inputs.** On voice, a companion skill whose own inputs bind a declared key that the call did not start with is refused, whether or not the key is later set, because skill inputs are framed into the skill's prompt. Bind the key on the tool that calls your system instead.
* **Scrubbed from tool output.** Where a value is at least four characters long, it is replaced with `[<key>]` in tool results and error text the model sees, including encoded forms such as a URL that echoes it. Shorter values are not scrubbed. Do not echo the value back in your API responses if you can avoid it.
* **Kept only for the call.** The value expires automatically shortly after the platform's maximum call duration.

## Verify in Staging

1. Declare the key on a staging service and bind it on a read-only test tool that reports the parameter it received.
2. Place a test call and find its `call_sid` with the matching rule above.
3. Send a synthetic value such as `TEST-PATIENT-0001`. Expect `200` with `bound`. Send it again: `200` with `unchanged`. Send `TEST-PATIENT-0002`: `409`.
4. Ask the agent something that needs the tool. Confirm the test tool received `TEST-PATIENT-0001`.
5. Confirm the agent never says the value and that it does not appear in the call transcript.
6. Try the request with a key that lacks the permission (`403`) and with a made-up `call_sid` (`404`).

Model narration alone does not prove which value reached the tool; check the tool's own record. Remove the test key and binding afterwards if the configuration is temporary.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.concurrence.com/developer-guide/guides/live-call-context.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
