# Sections API

> Read and replace an agent's instruction sections over the REST API, for example to keep them in sync with files in a Git repository.

Source: https://docs.runbear.io/api/sections

Last updated: 2026-10-13

The **Sections API** reads and writes an agent's instruction [sections](/agents/instructions.md): the named blocks of guidance the agent reads after its main instructions, in order. Use it to keep sections in version control and publish them from CI, so the agent's guidance changes through the same review process as your code.

The main instructions themselves are the agent's `systemPrompt`, set through the [Agents API](/api/agents.md).

Authenticate with a bearer API key; see [API keys](/api/api-keys.md). Reading sections needs no particular capability. `PUT`, `POST`, `PATCH`, and `DELETE` require the [`manageAgents` capability](/api/api-keys.md#capabilities). Full request/response schemas live in the [OpenAPI reference](https://api.runbear.io/v1/docs).

## Endpoints

| Endpoint                                           | Purpose                                                                |
| -------------------------------------------------- | ---------------------------------------------------------------------- |
| `GET /v1/agents/{agentId}/sections`                | List the agent's sections in prompt order                              |
| `PUT /v1/agents/{agentId}/sections`                | Replace the full list of sections, matched by name                     |
| `POST /v1/agents/{agentId}/sections`               | Add one section at the end. Answers `201` with the section             |
| `PATCH /v1/agents/{agentId}/sections/{sectionId}`  | Rename a section or change its content. Answers `200` with the section |
| `DELETE /v1/agents/{agentId}/sections/{sectionId}` | Delete a section. Answers `204`                                        |

Sections are managed through the API on Anthropic, OpenAI Responses, Gemini, and Claude Agent SDK agents (Claude Agent SDK agents only when your organization has API access to them). Every endpoint answers `404` with `error: "agent_not_found"` for an agent that doesn't exist in your organization or isn't one of these types, and the `{sectionId}` endpoints answer `404` with `error: "section_not_found"` for a section that doesn't belong to that agent. A write with a key that lacks `manageAgents` answers `403`.

## A section

```json
{
  "id": "6f1c2a9e-3b7d-4c55-9a0e-1d2f3a4b5c6d",
  "name": "Tone",
  "content": "Answer in short, plain sentences. Avoid jargon.",
  "position": 0,
  "createdAt": "2026-10-01T09:30:00.000Z",
  "updatedAt": "2026-10-13T14:05:12.000Z"
}
```

| Field                    | Meaning                                                                                                                                 |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                     | The section's id, used by `PATCH` and `DELETE`                                                                                          |
| `name`                   | The section's title, 1 to 200 characters. Section names should be unique within the agent (see [Limits and errors](#limits-and-errors)) |
| `content`                | The guidance itself, 1 to 100,000 characters                                                                                            |
| `position`               | Read-only. The section's place in the prompt, starting at 0 (see [Order](#order))                                                       |
| `createdAt`, `updatedAt` | When the section was created and last changed                                                                                           |

`GET` returns the list in the order the agent reads it:

```bash
curl -s https://api.runbear.io/v1/agents/$AGENT_ID/sections \
  -H "Authorization: Bearer $RUNBEAR_API_KEY"
```

```json
{
  "sections": [
    { "id": "6f1c…", "name": "Tone", "content": "Answer in short, plain sentences.", "position": 0, "createdAt": "…", "updatedAt": "…" },
    { "id": "a03e…", "name": "When to escalate", "content": "Hand off to a human when…", "position": 1, "createdAt": "…", "updatedAt": "…" }
  ]
}
```

## Replacing all sections

`PUT` takes the complete list of sections the agent should have and makes the agent match it in one step:

```bash
curl -s -X PUT https://api.runbear.io/v1/agents/$AGENT_ID/sections \
  -H "Authorization: Bearer $RUNBEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sections": [
      { "name": "Tone", "content": "Answer in short, plain sentences." },
      { "name": "When to escalate", "content": "Hand off to a human when…" }
    ]
  }'
```

It answers with the resulting list, in the same shape as `GET`. Sections are matched to the agent's existing ones by `name`:

- A section whose name already exists is updated in place and keeps its `id`. If neither its content nor its position changed, it is left untouched.
- A section with a new name is created.
- An existing section whose name is not in the payload is **deleted**. Sending an empty list deletes every section.
- The order of the array becomes the order the agent reads the sections in.

A `PUT` that changes nothing changes nothing: no section is rewritten and no new configuration version is recorded. Only the sections that actually changed show up in the agent's history, so running the same `PUT` from every CI build is safe.

Because matching is by name, renaming a section through `PUT` deletes the old section and creates a new one with a new `id`. Use `PATCH` to rename a section and keep its `id`.

## Adding, editing, and deleting one section

`POST` adds one section at the end of the list:

```bash
curl -s -X POST https://api.runbear.io/v1/agents/$AGENT_ID/sections \
  -H "Authorization: Bearer $RUNBEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Product vocabulary", "content": "\"Workspace\" means a Slack workspace." }'
```

`PATCH` changes a section's `name`, its `content`, or both; send at least one, and no other fields. It does not move the section. To change the order, send the full list with `PUT`.

```bash
curl -s -X PATCH https://api.runbear.io/v1/agents/$AGENT_ID/sections/$SECTION_ID \
  -H "Authorization: Bearer $RUNBEAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "content": "Answer in two sentences or fewer." }'
```

`DELETE` removes one section; the others keep their order.

## Order

Sections are read in the order set by the last `PUT`, and `position` reports each section's index in that order. Sections added later, by `POST` or in the dashboard, go to the end, oldest first. The dashboard has no way to reorder sections; use `PUT`.

## Limits and errors

| Limit                 | Value                   |
| --------------------- | ----------------------- |
| Section `name`        | 1 to 200 characters     |
| Section `content`     | 1 to 100,000 characters |
| Sections in one `PUT` | At most 100             |
| Request body          | At most 10 MiB          |

A request over the name, content, or section-count limits answers `400`; a body over 10 MiB answers `413`. Either way nothing changes.

| Status | `error`                                | When                                                                                                                                                                                                                            |
| ------ | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `duplicate_section_names`              | A `PUT` lists the same `name` more than once. The body's `names` field lists them, and nothing is changed                                                                                                                       |
| `400`  | `bad_request`                          | The body failed validation: a value over one of the limits above, an empty `name` or `content`, or a `PATCH` with no fields or an unknown field. The body carries `code: "FST_ERR_VALIDATION"` and a `message` naming the field |
| `403`  | `forbidden`                            | The API key lacks the `manageAgents` capability on a write (`code: "forbidden_by_key_role"`), or the agent is outside the key's allowlist (`code: "forbidden_by_agent_allowlist"`)                                              |
| `404`  | `agent_not_found`, `section_not_found` | See [Endpoints](#endpoints)                                                                                                                                                                                                     |
| `409`  | `existing_duplicate_section_names`     | A `PUT` on an agent that already has more than one section with the same name. The body's `names` field lists them                                                                                                              |
| `409`  | `section_name_taken`                   | A `POST`, or a `PATCH` that renames a section, uses a name another section on the agent already has                                                                                                                             |

The 100,000-character limit applies to every section in a `PUT`, including one that was saved longer in the dashboard: shorten it before syncing it through the API.

`PUT` matches sections by name, so it refuses to run while the agent has duplicate names. Rename or delete the extra sections, in the dashboard or with `PATCH` and `DELETE`, then retry.

Most errors use the shape `{ "error": "<code>", "message": "..." }`; see [Errors and limits](/api/errors-and-limits.md).

## Syncing sections from a repository

Keep one Markdown file per section, for example `agent-sections/01-tone.md`, `agent-sections/02-escalation.md`. This GitHub Actions workflow publishes them on every push to `main`: the file's name, without its numeric prefix and extension, becomes the section name, and the file order becomes the prompt order. Store the API key and agent id as repository secrets.

```yaml
name: Sync agent sections

on:
  push:
    branches: [main]
    paths: ["agent-sections/**"]

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build payload
        run: |
          for f in agent-sections/*.md; do
            name=$(basename "$f" .md | sed -E 's/^[0-9]+-//')
            jq -n --arg name "$name" --rawfile content "$f" \
              '{name: $name, content: $content}'
          done | jq -s '{sections: .}' > payload.json

      - name: Replace sections
        env:
          RUNBEAR_API_KEY: ${{ secrets.RUNBEAR_API_KEY }}
          AGENT_ID: ${{ secrets.RUNBEAR_AGENT_ID }}
        run: |
          curl -sS --fail-with-body -X PUT \
            "https://api.runbear.io/v1/agents/$AGENT_ID/sections" \
            -H "Authorization: Bearer $RUNBEAR_API_KEY" \
            -H "Content-Type: application/json" \
            --data @payload.json
```

The shell sorts `agent-sections/*.md` alphabetically, so the numeric prefixes control the order. Remember that `PUT` deletes any section not in the payload: once you sync from a repository, add and edit sections there rather than in the dashboard.

## Related

- [Instructions](/agents/instructions.md) - main instructions and sections in the dashboard
- [Agents API](/api/agents.md) - create and configure agents, including the main instructions
- [API keys](/api/api-keys.md) - create a key with the `manageAgents` capability
- [Errors and limits](/api/errors-and-limits.md) - error shapes and rate limits
