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

# Read and update an agent rich text prompt via API

> Read an agent rich text prompt from composition_by_language in GET /v2/agent, edit its text, variable, tool and language_value nodes, and save with PUT.

Agents built in the platform's prompt editor store their prompt as **rich text**: a list of typed nodes (plain text, variables, tool references and per-language wording), not one plain string. The API exposes that prompt as `composition_by_language`. Read it with [`GET /v2/agent/{agent_id}`](https://www.bolna.ai/docs/api-reference/agent/v2/get), edit it, and send it back with [`PUT /v2/agent/{agent_id}`](https://www.bolna.ai/docs/api-reference/agent/v2/update).

```text theme={"system"}
GET /v2/agent/{agent_id}   →  composition_by_language   (the prompt calls run on)
PUT /v2/agent/{agent_id}   ←  composition_by_language   (saves a new version)
```

***

## What is `composition_by_language`

`composition_by_language` is an object keyed by language code. Each value is an ordered list of **nodes**. Bolna joins the nodes in order to build the system prompt the agent runs on.

```json theme={"system"}
{
  "composition_by_language": {
    "en": [
      { "type": "text", "text": "You are a support agent for Acme. Greet " },
      { "type": "variable", "scope": "execution", "name": "customer_name" },
      { "type": "text", "text": " by name. If they ask for a human, use " },
      { "type": "function_tool", "name": "transfer_call" },
      { "type": "text", "text": "." }
    ]
  }
}
```

At call time this renders to:

```text theme={"system"}
You are a support agent for Acme. Greet {customer_name} by name. If they ask for a human, use transfer_call.
```

`{customer_name}` is then filled from the call's variables, exactly like a variable in a plain-text prompt. See [Dynamic variables](https://www.bolna.ai/docs/prompting/using-context.md).

### Node types

Every node has a `type`. No other keys than the ones listed below are accepted.

| `type` | Fields | Renders as |
| - | - | - |
| `text` | `text` (string) | The text, as written. |
| `variable` | `scope`, `name` | `{name}`, filled with the variable's value on the call. |
| `function_tool` | `name` | The tool's name, so the prompt can tell the LLM when to call it. |
| `language_value` | `name`, `values` | The entry in `values` for the language the call is in. |

**`variable`**

* `name`: the variable name. Dotted and indexed paths are allowed, for example `prior.loans.0.amount`.
* `scope`: one of `execution`, `system` or `agent_context`. Use `execution` for variables you pass when starting a call (`user_data` or batch CSV columns) and `system` for built-in variables such as `current_date` or `timezone`.

**`function_tool`**

* `name`: the name of a tool configured on the agent, such as `transfer_call`. Letters, digits and underscores only, and it can't start with a digit.

**`language_value`**

* `name`: a slot name, by convention prefixed with `language_` (for example `language_greeting`). Letters, digits and underscores only.
* `values`: the wording for each language, keyed by language code: `{ "en": "...", "hi": "..." }`. A value can also be a list of phrasings, such as `["okay", "alright"]`.

### The two shapes

How the object is keyed depends on how the agent's prompt is built.

**Single prompt (one key)**

Agents using the [Single Prompt view](https://www.bolna.ai/docs/agent-setup/single-prompt-view.md) have **one key**, the agent's default language. The rules and flow are shared `text` nodes; every line that is spoken differently per language is a `language_value` node carrying all its translations.

```json theme={"system"}
{
  "composition_by_language": {
    "hi": [
      { "type": "text", "text": "Open the call with: " },
      {
        "type": "language_value",
        "name": "language_greeting",
        "values": {
          "hi": "नमस्ते, मैं Acme से बोल रही हूँ।",
          "en": "Hello, I'm calling from Acme."
        }
      },
      { "type": "text", "text": "\nAlways confirm the order ID before sharing details." }
    ]
  }
}
```

On a Hindi call the greeting renders as the `hi` value, on an English call as the `en` value. A language missing from `values` renders as `{language_greeting}`, so fill in every language the agent speaks.

**One prompt per language**

Agents on the [older canvas](https://www.bolna.ai/docs/agent-setup/agent-tab.md#canvas) have **one key per language**, each holding that language's full prompt.

```json theme={"system"}
{
  "composition_by_language": {
    "en": [
      { "type": "text", "text": "You are a friendly support agent for Acme. Speak in English." }
    ],
    "hi": [
      { "type": "text", "text": "आप Acme के सपोर्ट एजेंट हैं। हिंदी में बात करें।" }
    ]
  }
}
```

A single-language agent has just one key.

Keep the shape the agent already uses: edit the nodes you get back from `GET` instead of switching between the two shapes.

***

## Read the prompt

`GET /v2/agent/{agent_id}` returns `composition_by_language` as a top-level field, next to `agent_prompts`.

```bash theme={"system"}
curl https://api.bolna.ai/v2/agent/<agent_id> \
  -H "Authorization: Bearer <api_key>"
```

```json Response (trimmed) theme={"system"}
{
  "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
  "agent_name": "Acme support",
  "tasks": [ ... ],
  "agent_prompts": {
    "task_1": { "system_prompt": "You are a support agent for Acme. Greet {customer_name} by name..." }
  },
  "composition_by_language": {
    "en": [
      { "type": "text", "text": "You are a support agent for Acme. Greet " },
      { "type": "variable", "scope": "execution", "name": "customer_name" },
      { "type": "text", "text": " by name..." }
    ]
  }
}
```

* The composition is the **published** one for the agent version returned, the same prompt live calls use. Unsaved edits in the platform editor aren't included.
* `GET /v2/agent/{agent_id}/versions/{agent_version_id}` returns the composition of that specific version.
* `composition_by_language` is `null` when the agent has no rich text prompt. Its prompt is then the plain-text `agent_prompts.task_1.system_prompt`, which you update as before.

***

## Update the prompt

Send the edited `composition_by_language` as a top-level key in the [`PUT /v2/agent/{agent_id}`](https://www.bolna.ai/docs/api-reference/agent/v2/update) body, alongside `agent_config` and `agent_prompts`.

**1. Get the agent**

Call `GET /v2/agent/{agent_id}` and keep `composition_by_language`.

**2. Edit the nodes**

Change `text`, add or remove `variable` and `function_tool` nodes, or update the wording inside a `language_value` node's `values`. Keep every language key and every node you aren't changing.

**3. Send it back with PUT**

Include the **full** `composition_by_language` in the `PUT` body, with the agent's `agent_config` and `agent_prompts`.

```bash theme={"system"}
curl -X PUT https://api.bolna.ai/v2/agent/<agent_id> \
  -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_config": { ... },
    "agent_prompts": {
      "task_1": {
        "system_prompt": "You are a support agent for Acme. Greet {customer_name} by name and offer help with their order. If they ask for a human, use transfer_call."
      }
    },
    "composition_by_language": {
      "en": [
        { "type": "text", "text": "You are a support agent for Acme. Greet " },
        { "type": "variable", "scope": "execution", "name": "customer_name" },
        { "type": "text", "text": " by name and offer help with their order. If they ask for a human, use " },
        { "type": "function_tool", "name": "transfer_call" },
        { "type": "text", "text": "." }
      ]
    }
  }'
```

Returns HTTP **200** like any agent update. The composition is saved with the new agent version, and the next calls run on it.

> [!WARNING]
>
> **Always send `composition_by_language` on `PUT` for an agent that has one.** Each `PUT` creates a new agent version, and only the composition sent in that request is linked to it. A `PUT` without it leaves the new version with no rich text prompt, so `GET` returns `null` and calls fall back to the plain-text `agent_prompts.task_1.system_prompt`.

### Rules for the payload

* **The whole object is replaced.** `composition_by_language` isn't merged with what's stored. A language key you leave out is removed from the prompt.
* **`agent_prompts` stays the plain-text fallback.** While a composition is linked, calls use the composition. Keep `agent_prompts.task_1.system_prompt` in step with it (the prompt rendered as plain text) so the fallback matches.
* **Empty means no rich text prompt.** Sending `{}`, or only empty node lists, saves no composition; the agent runs on `agent_prompts`.
* **Nodes are validated.** An unknown `type`, a missing field, an extra key or an invalid `name` returns HTTP **400** with a `message` and a `validation_errors` list pointing to the node.

> [!NOTE]
>
> [`PATCH /v2/agent/{agent_id}`](https://www.bolna.ai/docs/api-reference/agent/v2/patch_update) doesn't accept `composition_by_language`. Use `PUT` to change a rich text prompt. On an agent with a rich text prompt, patching `agent_prompts` changes only the plain-text fallback, not what calls say.

***

## Next steps

> **[Update agent](https://www.bolna.ai/docs/api-reference/agent/v2/update.md)**
>
> Full `PUT /v2/agent/{agent_id}` reference.

> **[Get agent](https://www.bolna.ai/docs/api-reference/agent/v2/get.md)**
>
> Full `GET /v2/agent/{agent_id}` reference.

> **[Single Prompt view](https://www.bolna.ai/docs/agent-setup/single-prompt-view.md)**
>
> How marked lines and translations work in the editor.

> **[Dynamic variables](https://www.bolna.ai/docs/prompting/using-context.md)**
>
> Pass variable values when starting a call.


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