---
title: "Create an agent"
description: "Create a Raíz agent through the API, read it back, update it safely and assign it to a channel."
---

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

# Create an agent

A Raíz agent talks to your organization's contacts, on phone calls (`voice`) or on WhatsApp and web chat (`text`). This guide creates a text agent with [`raiz.agents.create`](/api/raiz/agents/create/), then reads it, changes it and puts it on a channel.

## Before you start

You need a credential for your organization that holds `raiz:agents:write` (see [Authentication](/en/developers/authentication/)). The examples read an API key from `ARBOL_API_KEY`.

| Operation | Request | Scopes (any one) |
|---|---|---|
| [`raiz.agents.create`](/api/raiz/agents/create/) | `POST /v1/raiz/agents` | `raiz:agents:write` |
| [`raiz.agents.get`](/api/raiz/agents/get/) | `GET /v1/raiz/agents/{agentId}` | `raiz:agents:read`, `raiz:agents:write` |
| [`raiz.agents.list`](/api/raiz/agents/list/) | `GET /v1/raiz/agents` | `raiz:agents:read`, `raiz:agents:write` |
| [`raiz.agents.update`](/api/raiz/agents/update/) | `PATCH /v1/raiz/agents/{agentId}` | `raiz:agents:write` |
| [`raiz.channels.update`](/api/raiz/channels/update/) | `PATCH /v1/raiz/channels/{channelId}` | `raiz:channels:write` |

Each accepts an API key, a machine token or a person's sign-in. An API key needs the scope among those chosen when it was created; a person's token counts only as far as their roles grant it.

## Create the agent

Only `modality` and `name` are required. Add `instructions`, `language` and `timeZone` so the agent knows its job and speaks in the right language and time zone:

```bash
curl https://api.getarbol.com/v1/raiz/agents \
  --request POST \
  --header "Authorization: Bearer $ARBOL_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "modality": "text",
    "name": "Appointment reminders",
    "instructions": "You remind patients of Example Clinic about upcoming appointments and confirm whether they will attend. You never give medical advice.",
    "language": "es-CO",
    "timeZone": "America/Bogota"
  }'
```

The body accepts only the fields its schema declares; any other field answers `400 INVALID_INPUT`. The optional ones:

| Field | Meaning |
|---|---|
| `instructions` | What the agent does and how: its role, what it may and may not do, the facts it relies on. Up to 100,000 characters. |
| `language` | BCP 47 language tag, such as `es-CO`. |
| `timeZone` | IANA time zone, such as `America/Bogota`. |
| `firstMessage` | Voice agents: what the agent says when a call connects. Text agents open WhatsApp conversations with `settings.greeting` instead. |
| `status` | `active` or `paused`. A paused agent answers nothing and places no calls. |
| `knowledgeDocumentIds` | Up to 100 ready Knowledge documents to consult, from [`raiz.knowledge.list`](/api/raiz/knowledge/list/). |
| `integrationTools` | Up to 200 `connectionId` and `tool` pairs: the tools of your [integrations](/en/developers/integrations/) the agent may call. |
| `integrationSkills` | Text agents: up to 32 `connectionId` and `skill` pairs that a connection publishes. |
| `skills` | Up to 32 text-only procedures, 256 KiB in total. |
| `settings` | How the agent behaves. Text and voice settings apply only to agents of that modality. |
| `description` | Up to 2,000 characters. |

A successful call answers `200` with the agent in `data`. The response below is abridged: the full object also carries `status`, `ready`, `settings`, `skills`, `integrationSkills` and `toolCount`.

```json
{
  "success": true,
  "data": {
    "id": "agt_01jd8x5k2m7q9r3t6v8w0y4z1b",
    "name": "Appointment reminders",
    "modality": "text",
    "version": 1,
    "instructions": "You remind patients of Example Clinic about upcoming appointments and confirm whether they will attend. You never give medical advice.",
    "language": "es-CO",
    "timeZone": "America/Bogota",
    "firstMessage": null,
    "description": null,
    "synced": true,
    "syncErrorCode": null,
    "channelCount": 0,
    "channels": [],
    "knowledgeDocumentIds": [],
    "integrationTools": [],
    "createdAt": "2026-10-30T15:04:05Z",
    "updatedAt": "2026-10-30T15:04:05Z"
  },
  "error": null,
  "metadata": { "requestId": "3f6c2a8e-1b4d-4e7a-9c0f-5d2e8b1a7c64" }
}
```

Keep `data.id`: every other operation on the agent takes it as `agentId`. Two fields say where the agent stands:

- `ready`: whether it can hold conversations. It must be `active` and have instructions, and a voice agent must also be published for calls.
- `synced`: whether calls already use the current version. Voice agents are published in the background, so check it after each change; `syncErrorCode` says why publishing failed. It is always `true` for text agents.

> **Don't resend a create blindly**
>
> `raiz.agents.create` is not idempotent. If the call times out or answers `502 OUTCOME_UNKNOWN`, the agent may already exist: list agents with `q` set to its name before you send the request again. See [Retries](/en/developers/concepts/retries/).

## Read it back

[`raiz.agents.get`](/api/raiz/agents/get/) takes `agentId` in the URL path and answers the agent's complete setup:

```bash
curl "https://api.getarbol.com/v1/raiz/agents/agt_01jd8x5k2m7q9r3t6v8w0y4z1b" \
  --header "Authorization: Bearer $ARBOL_API_KEY"
```

To find agents, [`raiz.agents.list`](/api/raiz/agents/list/) filters by `status`, `modality` and `q` (the name, ignoring accents and case), newest first:

```bash
curl "https://api.getarbol.com/v1/raiz/agents?modality=text&q=reminders" \
  --header "Authorization: Bearer $ARBOL_API_KEY"
```

The agents are in `data.data`, 25 per page by default; pass `data.pageInfo.nextCursor` as `cursor` for the next page. See [Pagination](/en/developers/concepts/pagination/).

## Update it

Put `agentId` in the URL path and send the fields to change and, as `expectedVersion`, the `version` you read:

```bash
curl https://api.getarbol.com/v1/raiz/agents/agt_01jd8x5k2m7q9r3t6v8w0y4z1b \
  --request PATCH \
  --header "Authorization: Bearer $ARBOL_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "expectedVersion": 1,
    "instructions": "You remind patients of Example Clinic about upcoming appointments, confirm whether they will attend and pass reschedule requests to the team. You never give medical advice."
  }'
```

- Fields you leave out stay as they are, and `null` clears a field that accepts it.
- `settings`, `integrationTools`, `integrationSkills`, `skills` and `knowledgeDocumentIds` replace the whole value. Read the agent first and send the complete new value; settings you leave out return to their default.
- With `expectedVersion`, the change applies only if nobody changed the agent since you read it. Otherwise it answers `409 VERSION_CONFLICT`: read the agent again and reapply your change.
- `"status": "paused"` stops the agent from answering and calling.
- `modality` changes only while the agent has no channels and no published voice line.

## Put it on a channel

Assign the agent to a WhatsApp number, phone number or web chat widget so it answers there. Find the channel with [`raiz.channels.list`](/api/raiz/channels/list/), then send its id and the agent's to [`raiz.channels.update`](/api/raiz/channels/update/):

```json
{
  "channelId": "chn_01jd8x6b3c4d5e6f7g8h9j0k1m",
  "agentId": "agt_01jd8x5k2m7q9r3t6v8w0y4z1b"
}
```

The agent's modality must fit the channel: text for WhatsApp and web chat, voice for phone numbers.

## Errors

| Status | Code | What to do |
|---|---|---|
| 400 | `INVALID_INPUT` | Fix the fields in `error.data.issues`; each `path` is a JSON Pointer to a refused field. |
| 400 | `AGENT_SKILLS_INVALID` | Skills need a text agent, unique names and resource paths, nonempty instructions and at most 256 KiB in total. |
| 403 | `FORBIDDEN` | The credential lacks the scope the operation needs. |
| 409 | `VERSION_CONFLICT` | Read the agent again and reapply your change. |
| 409 | `AGENT_MODALITY_LOCKED` | The agent has channels or a published voice line. Create a new agent instead. |
| 409 | `EVAL_GATE_UNMET` | The agent has `settings.evalGate` on: send `evalRunId`, a completed [evaluation run](/api/raiz/eval-runs/create/) of exactly this content in which every case passed. |
| 422 | `INTEGRATION_TOOL_UNAVAILABLE`, `INTEGRATION_SKILL_UNAVAILABLE` | A tool or skill isn't on that connection, or Raíz can't run it. Check [`account.connections.list`](/api/account/connections/list/). |
| 422 | `KNOWLEDGE_DOCUMENT_UNAVAILABLE` | A Knowledge document doesn't exist in this organization. |
| 422 | `AGENT_SETTINGS_INVALID` | A setting doesn't apply to the agent's modality, or two settings contradict each other. |
| 502 | `OUTCOME_UNKNOWN` | The change may have been applied: read the agent before you repeat it. |

Every failure carries `error.code`, `error.message`, `error.retryable` and `metadata.requestId`. See [Responses and errors](/en/developers/concepts/responses-and-errors/).

Source: https://docs.getarbol.com/en/developers/guides/create-an-agent/index.mdx
