# Response Components API

> Set how often an agent answers with an interactive response component, over the agents REST API.

Source: https://docs.runbear.io/api/response-components

Last updated: 2026-09-21

An agent can attach a **response component** to its reply in the
[Web SDK](/api/web-sdk.md#response-components) — a clickable `confirm`, a `select` list, or a formatted
`card`. How often it reaches for one is a per-agent setting, `responseComponents.frequency`, carried
on the public agents API.

Authenticate with a bearer API key, created under **Settings → API Keys**. Full request and
response schemas live in the [OpenAPI reference](https://api.runbear.io/v1/docs).

> **This dial governs the Web SDK only**
>
> Slack buttons are not affected by it. Slack offers the options on any turn a person is waiting on
> whenever the agent's **Choice Buttons** setting is on, whatever this dial says, and a card has no
> Slack renderer at all. If your agent answers only in Slack, setting this dial changes nothing — see
> [Choice buttons](/agents/settings.md#choice-buttons) instead.

## Endpoints

| Endpoint                | Purpose                                                                                                                          |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `GET /v1/agents?ids=…`  | Read the level for several agents. `ids` is **required** — a comma-separated list of up to 100 agent ids; omitting it is a `400` |
| `GET /v1/agents/{id}`   | Read the level for one agent                                                                                                     |
| `PATCH /v1/agents/{id}` | Set the level                                                                                                                    |

`PATCH` requires an API key with the `manageAgents` capability. Reads do not. That capability is the
public-surface equivalent of the dashboard's `feature:write` permission, which is a per-user
dashboard permission with no representation on an API key — so do not look for `feature:write` on a
key, and do not expect a key to inherit the dashboard permissions of whoever created it. Keys are
issued from the dashboard by an organization admin or owner.

`POST /v1/agents` does **not** accept `responseComponents`; the field is ignored rather than
rejected, so a create carrying it still returns `201` with the dial at `off`. Create the agent, then
set the dial with a follow-up `PATCH`.

## Reading the level

`responseComponents` is a required field on every agent payload — `GET /v1/agents`,
`GET /v1/agents/{id}`, and the create and update responses. Abridged to the relevant fields:

```json
{
  "id": "0f2b7d1e-9c34-4a5b-8f61-2ad0c5e7b913",
  "name": "Support agent",
  "longTermMemory": { "enabled": true },
  "responseComponents": { "frequency": "medium" }
}
```

An agent that has never had the dial set reads back as `"off"`.

## Setting the level

```bash
curl -X PATCH https://api.runbear.io/v1/agents/0f2b7d1e-9c34-4a5b-8f61-2ad0c5e7b913 \
  -H "Authorization: Bearer $RUNBEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"responseComponents":{"frequency":"medium"}}'
```

The response is the full agent, with `responseComponents.frequency` reflecting the write.

> **The request and response types are deliberately different**
>
> They are not one type, and a generated client should not treat them as one.
>
> - **On the response**, `frequency` is an **open string**. New levels may be appended, so do not
>   generate a closed union from it — the first appended level would break every client that had not
>   regenerated. The value is always a real level: a level this deploy does not recognize reads back
>   as `"off"`.
> - **On the request**, `frequency` is validated against the closed set below. An unrecognized value
>   is **rejected** with `400` `bad_request`, never coerced. `{"frequency":"HIGH"}` is an error, not a
>   silent write of a string that would resolve to `"off"` on every later read.

## The levels

| Level    | What it does                                                                                                                                                       |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `off`    | The agent is not offered the components at all on this channel. This is the default, and the value an unset or unrecognized stored level reads back as.            |
| `low`    | Offer buttons sparingly: only when the next reply is unambiguously one of a few concrete options, and at most once per turn.                                       |
| `medium` | The balanced setting. Offer buttons whenever the next reply is plausibly one of a few concrete options; answer in prose when the question is genuinely open-ended. |
| `high`   | Offer buttons at every reasonable opportunity — whenever the turn would end in a question, a list of suggestions, or any choice the visitor could type back.       |

> **The three non-off levels cost the same**
>
> `low`, `medium` and `high` change **how often the agent reaches for a component**, and nothing else.
> The tools registered for the turn are identical across all three; the only difference is a short
> paragraph of guidance in the agent's instructions. Do not read the levels as a cost gradient — there
> is none. Only `off` is different in kind: it withholds the tools, and with them the guidance.

## What else has to be true

The dial is necessary but not sufficient. A non-`off` level produces components in the Web SDK only
when all of these also hold:

- **The beta is enabled for your organization.** Response components are a closed beta; Runbear
  turns them on per organization. Until then a non-`off` level stores and reads back correctly and
  produces nothing.
- **The agent's Choice Buttons setting is on.** Turning it off withdraws interactive prompts on
  every surface, and it is checked before this dial. An agent opted out there never gains web
  components, whatever the level says.
- **Your widget is on an SDK release that carries the renderers.** On an earlier release the
  component arrives on the wire and the widget shows its prose fallback. Your Runbear contact
  confirms the minimum version when they enable the beta.

**Which components the agent can reach for also depends on its type.** `anthropic` and
`openai-responses` agents can emit all three; a `claude-agent-sdk` agent emits `confirm` and
`select` only, and never a `card`, at any level. `gemini` emits none, which is why the dial refuses
to leave `off` there at all.

Because the beta's per-organization switch and the Choice Buttons setting are separate, a `GET`
returning `"frequency": "high"` is a statement about this agent's configuration, not a promise that
a visitor will see a button.

## Errors

| Status | Body                                                      | Cause                                                                                 |
| ------ | --------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `400`  | `{ "error": "bad_request", … }`                           | `frequency` is not one of `off`, `low`, `medium`, `high`.                             |
| `422`  | `{ "error": "unsupported_for_agent_type", "message": … }` | A non-`off` level was written to an agent whose type cannot emit response components. |

The `422` is terminal for that agent: the dial is only meaningful on a type that supports the
components, and no retry changes that. On the public agents API this is refused for `gemini`, which
registers no tools at all. The agent types that predate this API — `perplexity`, `upstage` and
`openai-assistant` — also cannot emit components, but they are not exposed on `/v1/agents` and so
cannot be reached to be refused.

Writing `"off"` is accepted on **every** type, so an agent can always be returned to the default
even if it could not be given a level in the first place.

## Related

- [Web SDK](/api/web-sdk.md#response-components) — the rendering contract: the wire event, the props
  shapes, and what a click does
- [Choice buttons](/agents/settings.md#choice-buttons) — the dashboard switch this dial sits behind
- [API keys](/api/api-keys.md) — creating the key that carries `manageAgents`
- [OpenAPI reference](https://api.runbear.io/v1/docs) — exact schemas for the agents endpoints
