# Agents

> The agent document, field by field, for inbound and outbound agents; drafts, versions and numbers.


# Agents

An agent is one JSON document. The console editor, `allerto agents push`, `POST /v1/agents` and the MCP tool `create_agent` all take exactly this object. The machine-readable schema is at [`/v1/agents/schema`](/v1/agents/schema) and in the [reference](/docs/reference/).

## Common keys

| Key            | Type                               | Default                               | Meaning                                                                                 |
| -------------- | ---------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------- |
| `type`         | `inbound` \| `outbound`            | —                                     | Set at creation, never changes                                                          |
| `name`         | text (2–100)                       | —                                     | Internal name                                                                           |
| `persona`      | text (1–80)                        | —                                     | How the agent introduces itself ("Giulia")                                              |
| `language`     | `it` \| `en`                       | `it`                                  | Language of the conversation and of extracted text                                      |
| `model`        | `gpt-live-1` \| `gemini-3.8-live`  | `gpt-live-1`                          | Live voice model; `gemini-3.8-live` is required for `mcp` tools                         |
| `voice`        | voice id                           | model default                         | A voice of the chosen model (`bossa`… / `Erinome`…); or `customVoiceId` (GPT-Live only) |
| `instructions` | text (10–12000)                    | —                                     | What the agent does. Outbound agents may use `{{input}}` placeholders                   |
| `result`       | list of fields                     | `[]`                                  | Data extracted after the call (at least one for inbound)                                |
| `delivery`     | object                             | sandbox                               | Where `call.completed` goes: `sandbox`, `webhook`, `email` or `both`                    |
| `review`       | `on_issues` \| `always` \| `never` | inbound `on_issues`, outbound `never` | When an operator must check the data before it is delivered                             |
| `transfer`     | object                             | none                                  | Hand the call to an operator, see below                                                 |
| `mcp`          | object                             | none                                  | Your MCP server: its tools during the call, see [Tools during calls](tools.md)          |
| `recording`    | boolean                            | `false`                               | Record the audio of phone calls; the agent announces it, see [Privacy](privacy.md)      |

## Inbound keys

| Key        | Type           | Meaning                           |
| ---------- | -------------- | --------------------------------- |
| `greeting` | text (10–1000) | First sentence when someone calls |

Numbers belong to your account: Allerto buys them and assigns them to it. You route each one to any of your inbound agents with `PUT /v1/numbers/{id}` (`{"agent": "<id>"}`) or in the console, and every agent shows the numbers it answers on (`numbers` in the agent object). An agent from another account is refused with `different_account`.

Clients are an optional grouping (`GET`/`POST /v1/clients`). `DELETE /v1/clients/{id}` removes one: its agents are archived, its queued calls cancelled and its console login removed, while its calls and usage stay in your history. A client whose numbers are still active is refused with `client_has_numbers`; its `external_id` can be reused afterwards.

## Outbound keys

| Key                     | Type                  | Default                         | Meaning                                                                               |
| ----------------------- | --------------------- | ------------------------------- | ------------------------------------------------------------------------------------- |
| `opening`               | text with `{{input}}` | —                               | First sentence when the person answers                                                |
| `inputs`                | list of fields        | `[]`                            | Data you pass with every call; `required` ones are enforced                           |
| `calling.callerId`      | E.164                 | an active number of the account | Caller ID; must be one of your numbers, checked on publish and again before each call |
| `calling.identityCheck` | boolean               | `true`                          | Confirm the person before revealing why you call                                      |
| `calling.hours`         | `{days, from, to}`    | Mon–Sat 09:00–20:00             | Calling window, Italian time; within Mon–Sat 08:00–21:00                              |
| `calling.maxAttempts`   | 1–5                   | 3                               | Attempts when nobody answers (retries after 15 min, 1 h, 3 h)                         |
| `calling.autoCallback`  | boolean               | `true`                          | Call back automatically when the person asks for a later time                         |

## Transfer to an operator

```json
"transfer": {
  "to": "+390212345678",
  "when": "gas leaks, flooding or other emergencies",
  "hours": { "days": [1, 2, 3, 4, 5], "from": "09:00", "to": "18:00" },
  "announcement": "La metto in contatto con un operatore, resti in linea.",
  "musicUrl": "https://example.com/hold.mp3"
}
```

When the person asks to speak with an operator or a real person (or in the situations of `when`), the agent may ask once for their name and reason, then says the `announcement` and the call is transferred to `to`. Only `to` is required.

- `to`: the operator's number (E.164). It is the only number the agent ever transfers to, and it cannot be one of your Allerto numbers.
- `hours`: Italian time, any day of the week. Outside them the agent says nobody is available and takes the request. Without `hours` the transfer is always available.
- `announcement`: said word for word; the default is "La metto in contatto con un operatore, resti in linea." (Italian) or "I'm putting you through to a colleague, please hold." (English).
- `musicUrl`: an https audio file played while the operator's phone rings; without it the person hears the normal ringing tone.

The operator sees the Allerto number the person called (Italian networks block calls from abroad that show an Italian mobile number, so the caller's own number cannot be passed through); the caller's number is in the call data. If nobody answers within 25 seconds, the person hears that no operator is available and that their request has been recorded. A transfer lasts at most 30 minutes. The call keeps what was collected before the transfer, with `outcome: "transferred"` and a `transfer` object (see [Calls](calls.md)).

## Fields

`inputs` and `result` use the same field shape:

```json
{
  "key": "pay_by",
  "label": "Promised date",
  "type": "date",
  "required": false,
  "options": [],
  "when": { "field": "answer", "equals": "will_pay" },
  "description": "Only if they commit to a date"
}
```

| Key           | Meaning                                                                             |
| ------------- | ----------------------------------------------------------------------------------- |
| `key`         | Lowercase letters, digits and `_`; unique in the list. Appears in data and webhooks |
| `label`       | Human name, also used by the agent when asking                                      |
| `type`        | `text`, `number`, `boolean`, `select`, `date` (YYYY-MM-DD), `phone`, `email`        |
| `required`    | Missing required values become `issues` (and trigger review for inbound agents)     |
| `options`     | Allowed values for `select`                                                         |
| `when`        | Result fields only: required only when another field has a value                    |
| `description` | Extra guidance for extraction                                                       |

Domain outcomes (promise to pay, dispute, confirmed appointment) belong in a `select` result field. The call `outcome` stays generic across sectors.

## Examples

Inbound:

```json
{
  "type": "inbound",
  "name": "Fault reports",
  "persona": "Giulia",
  "greeting": "Good morning, this is Giulia, the virtual assistant of Studio Ferrario. How can I help?",
  "instructions": "Collect fault reports for the buildings we manage. Ask one question at a time.",
  "result": [
    { "key": "caller_name", "label": "Name", "type": "text", "required": true },
    {
      "key": "callback_phone",
      "label": "Callback number",
      "type": "phone",
      "required": true
    },
    {
      "key": "address",
      "label": "Building address",
      "type": "text",
      "required": true
    },
    {
      "key": "description",
      "label": "Description",
      "type": "text",
      "required": true
    }
  ],
  "delivery": { "mode": "email", "email": { "to": ["office@example.com"] } }
}
```

Outbound: see the [Quickstart](/docs/quickstart/).

## Drafts, versions and concurrency

| Step    | API                                                | CLI                                 |
| ------- | -------------------------------------------------- | ----------------------------------- |
| Create  | `POST /v1/agents {"config", "client"?}`            | `allerto agents push file.json`     |
| Edit    | `PUT /v1/agents/{id}/draft {"config", "revision"}` | `allerto agents push file.json`     |
| Check   | `POST /v1/agents/validate {"config"}`              | `allerto agents validate file.json` |
| Preview | `POST /v1/agents/{id}/preview {"inputs"}`          | `allerto agents preview <id>`       |
| Publish | `POST /v1/agents/{id}/publish {"revision"}`        | `allerto agents publish <id>`       |
| History | `GET /v1/agents/{id}/versions`                     | —                                   |
| Archive | `POST /v1/agents/{id}/archive`                     | `allerto agents archive <id>`       |

Every save needs the current `revision`. If someone else saved in between you get `409 revision_conflict` with the current revision in `details`: reload, merge, save again. Publishing creates an immutable version; calls always record which version spoke.

Validation errors carry a `path` per problem (for example `opening` or `result.2.when`), so a program can point at the exact field.
