# Settings reference

> Every field of an agent's configuration — type, default, limits and what it does — exactly as GET /v1/agents/{agent} returns it.

An agent's configuration is one JSON document. `GET /v1/agents/{agent}` always returns it **complete**, with every default filled in; unknown fields are rejected. Edit it in the dashboard's single-page editor or with a [merge patch](/docs/en/concepts/agents-and-versions/#edit-the-draft). The JSON Schema, with English and Arabic descriptions, is at `GET /v1/agents/config_schema`.

Limits on text are counted in **characters** (Unicode code points), so Arabic and English get the same room.

## The whole document

A new agent's configuration, with its defaults:

```json
{
  "locale": { "language": "en", "timezone": "Asia/Riyadh" },
  "identity": { "enabled": false, "bot_name": "", "dialect": "match", "tone": "friendly", "persona_notes": "" },
  "instructions": { "enabled": true, "text": "" },
  "knowledge": { "sources": [] },
  "tools": {
    "enabled": true,
    "builtin": {
      "transfer_to_human": { "enabled": true, "rules": "" },
      "create_ticket": { "enabled": false, "rules": "" },
      "flag_conversation": { "enabled": false, "rules": "" },
      "search_knowledge": { "enabled": true, "rules": "" }
    },
    "http": [],
    "client": [],
    "allow_anonymous_actions": false,
    "max_tool_rounds": 3
  },
  "guardrails": {
    "never_handle": {
      "enabled": true,
      "items": [
        "Damage, injury or accident claims",
        "Legal threats, or mentions of lawyers, police or authorities",
        "Refund or compensation amounts",
        "Complaints that name an employee",
        "Requests for another customer's data"
      ]
    },
    "escalation_reply": { "ar": "", "en": "" },
    "business_scope": ""
  },
  "handoff": {
    "ttl_hours": 24,
    "never_expire": false,
    "outside_hours": "record",
    "destinations": [{ "type": "desk" }],
    "notify": { "emails": [], "unclaimed_reminder_minutes": 10 },
    "on_expire": "release_with_message",
    "expire_message": { "ar": "", "en": "" }
  },
  "business_hours": {
    "enabled": false,
    "schedule": [
      { "day": "sun", "open": "09:00", "close": "17:00" },
      { "day": "mon", "open": "09:00", "close": "17:00" },
      { "day": "tue", "open": "09:00", "close": "17:00" },
      { "day": "wed", "open": "09:00", "close": "17:00" },
      { "day": "thu", "open": "09:00", "close": "17:00" }
    ],
    "out_of_hours_message": { "ar": "", "en": "" },
    "outside_hours_ai": "answer"
  },
  "conversation": { "history_limit": 12, "idle_timeout_minutes": 30, "concurrency": "queue", "stream_mode": "auto" },
  "model": {
    "provider": "openai",
    "model": "gpt-6-luna",
    "credential": "auto",
    "max_reply_tokens": 1024,
    "temperature": null,
    "fallback_models": []
  },
  "variables": [],
  "overrides": { "allowed": [], "models": [] },
  "fallback": { "message": { "ar": "", "en": "" }, "handoff_on_failure": true },
  "widget": {
    "greeting": { "ar": "", "en": "" },
    "launcher_label": { "ar": "", "en": "" },
    "theme": { "accent": "#0F766E", "position": "end" },
    "anonymous_daily_conversations": 200
  }
}
```

### Texts in two languages

Fields that customers read word for word are **localized texts**: an object `{"ar": "…", "en": "…"}`. K-Agent picks the language of the customer's latest message (30% or more Arabic letters means Arabic), falling back to `locale.language`. A blank value uses the built-in default for that language, where one exists. Localized texts are `guardrails.escalation_reply`, `fallback.message`, `handoff.expire_message`, `business_hours.out_of_hours_message`, `widget.greeting` and `widget.launcher_label`.

## Agent fields

These sit beside the configuration, at the top level of the agent object.

| Field | Type | Default | Description |
|---|---|---|---|
| `name` | string | — | Display name, required. |
| `slug` | string | from the name | URL name, `^[a-z0-9][a-z0-9-]{0,62}$`, unique per project. Usable wherever `{agent}` appears. |
| `description` | string | `""` | For your team; never sent to the model. |
| `ai_paused` | boolean | `false` | Stops AI replies at once. Not versioned; no `If-Match` needed; audited. |
| `actions_paused` | boolean | `false` | Removes tools with side effects at once. Not versioned; audited. |

## locale

| Field | Type | Default | Description |
|---|---|---|---|
| `language` | `ar` · `en` | project's language | The agent's main language: the fallback for localized texts and the default widget direction. |
| `timezone` | IANA name | project's time zone (`Asia/Riyadh`) | Drives the agent's clock, business hours, `next_open_local` and notices. Never the server's time zone. |

## identity

| Field | Type | Default | Description |
|---|---|---|---|
| `enabled` | boolean | `false` | Turns the identity block on. When off it adds nothing to the prompt. |
| `bot_name` | string | `""` | The name the agent gives when asked who it is. |
| `dialect` | `match` · `saudi` · `gulf` · `msa` · `english` | `match` | The language and dialect of replies. `match` mirrors the customer. See [Arabic dialect and tone](/docs/en/guides/arabic-dialect-and-tone/). |
| `tone` | `friendly` · `formal` · `brief` | `friendly` | The register of replies. |
| `persona_notes` | string, ≤ 4,000 | `""` | Extra personality notes. `{{variables}}` allowed. |

## instructions

| Field | Type | Default | Description |
|---|---|---|---|
| `enabled` | boolean | `true` | Off keeps the text but leaves it out of the prompt. |
| `text` | string, ≤ 20,000 | `""` | Your instructions: what the business does, how to answer, what to avoid. `{{variables}}` are rendered as clearly marked data. |

## knowledge

| Field | Type | Default | Description |
|---|---|---|---|
| `sources` | array | `[]` | `{source_id, mode}` items, in the order they appear in the prompt. `mode` is `always` (in the prompt) or `searchable` (behind `search_knowledge`). See [Knowledge](/docs/en/concepts/knowledge/). |

## tools

| Field | Type | Default | Description |
|---|---|---|---|
| `enabled` | boolean | `true` | Master switch. Off removes every tool but keeps the grants below. |
| `builtin.transfer_to_human` | `{enabled, rules}` | on | Hand the conversation to your team. |
| `builtin.create_ticket` | `{enabled, rules}` | off | Open a ticket. |
| `builtin.flag_conversation` | `{enabled, rules}` | off | Flag the session for review. |
| `builtin.search_knowledge` | `{enabled, rules}` | on | Search the `searchable` knowledge. |
| `http` | array | `[]` | `{tool_id, rules}` grants of [HTTP tools](/docs/en/guides/http-tools/). |
| `client` | array | `[]` | `{name, description, parameters, rules, effect}` [client tools](/docs/en/guides/client-tools/). `effect` is `read` or `action` (default). |
| `allow_anonymous_actions` | boolean | `false` | Let tools with side effects run for end users who are not verified. |
| `max_tool_rounds` | integer, 1–5 | `3` | Model-and-tools rounds per turn before a final answer without tools. |

`rules` (every grant) is up to 4,000 characters and is added to that tool's description as your company's rules for it.

## guardrails

| Field | Type | Default | Description |
|---|---|---|---|
| `never_handle.enabled` | boolean | `true` | The never-handle block, placed last in the prompt. |
| `never_handle.items` | array of strings | 5 localized defaults | Situations the agent must hand over instead of answering. An emptied list stays empty. |
| `escalation_reply` | localized text, ≤ 500 each | empty | Sent **word for word** when a liability handoff succeeds. Empty means the agent words it itself; the dashboard suggests a text you can accept. |
| `business_scope` | string | `""` | One or two sentences on what this agent is for. |

None of these can be changed by per-request overrides. See [Handoff and safety](/docs/en/concepts/handoff-and-safety/).

## handoff

| Field | Type | Default | Description |
|---|---|---|---|
| `ttl_hours` | integer, 1–720 | `24` | How long an open handoff keeps the AI quiet. Claims and team messages extend it. `0` is rejected; use `never_expire`. |
| `never_expire` | boolean | `false` | Keep handoffs open until someone resolves them. |
| `outside_hours` | `record` · `withhold` | `record` | Out of business hours, still record handoffs with honest wording (`record`), or remove `transfer_to_human` (`withhold`). |
| `destinations` | array | `[{"type": "desk"}]` | Where agent handoffs go, in order: `desk` or `webhook`. Handoffs requested by the API, by policy or by your team always reach the Desk. |
| `notify.emails` | array of emails | `[]` | Alert addresses for new and unclaimed handoffs (needs email to be configured on the server). |
| `notify.unclaimed_reminder_minutes` | integer | `10` | When to send `handoff.unclaimed` for a handoff nobody has claimed. |
| `on_expire` | `release_with_message` · `close` | `release_with_message` | On expiry, give the conversation back to the agent with a notice, or close it. |
| `expire_message` | localized text | built-in default | The notice the customer sees when a handoff expires. |

## business_hours

| Field | Type | Default | Description |
|---|---|---|---|
| `enabled` | boolean | `false` | Tell the agent when your team is online. |
| `schedule` | array | Sun–Thu 09:00–17:00 | `{day, open, close}` with `day` in `sun`…`sat` and 24-hour `HH:MM`. A span with `close` ≤ `open` runs past midnight and belongs to its opening day. Days not listed are closed; enabled with an empty schedule means always open. |
| `out_of_hours_message` | localized text | built-in default | Used for handoffs out of hours, and sent as the whole reply with `message_only`. |
| `outside_hours_ai` | `answer` · `message_only` | `answer` | Out of hours, keep answering with AI, or send `out_of_hours_message` word for word with no AI call. |

Business hours use `locale.timezone`. When enabled, the agent is told whether your team is online now or when it is back.

## conversation

| Field | Type | Default | Description |
|---|---|---|---|
| `history_limit` | integer, 2–100 | `12` | How many recent messages of the session the model sees. The window starts at a user message. |
| `idle_timeout_minutes` | integer | `30` | Idle gap that starts a new [segment](/docs/en/concepts/sessions/#history-and-segments). It never cuts history. |
| `concurrency` | `queue` · `reject` · `interrupt` | `queue` | Default policy for new API sessions. Widget sessions use `interrupt`. |
| `stream_mode` | `auto` · `live` · `buffered` | `auto` | `live` streams every token; `buffered` sends text per round; `auto` streams live unless an escalation reply is set, then buffers. |

## model

| Field | Type | Default | Description |
|---|---|---|---|
| `provider` | `openai` · `anthropic` · `deepseek` · `gemini` · `openai_compatible` | server default | The model provider. |
| `model` | string | server default | A model ID from `GET /v1/models`, which also shows each model's tier, weight and whether your plan allows it. |
| `credential` | `auto` · `platform` · `pcred_…` | `auto` | `auto` uses the platform's key for the provider if there is one, otherwise your project's key for it. `pcred_…` pins one of your own provider keys (Settings → Model providers, or `POST /v1/provider_credentials`). |
| `max_reply_tokens` | integer | `1024` | Budget for the visible reply per model call. Reasoning models get extra headroom on top automatically. |
| `temperature` | number or `null` | `null` | Sampling temperature, ignored for models that don't accept one. |
| `fallback_models` | array | `[]` | `{provider, model}` items tried in order when the main model fails. |

## variables

Declare values your app passes per session or per message, then use them as `{{name}}` in instructions or persona notes:

```json
{
  "variables": [
    { "name": "customer_name", "type": "string", "required": false, "default": "", "secret": false, "client_settable": true, "description": "First name, for greetings" },
    { "name": "loyalty_tier", "type": "string", "required": false, "default": "standard", "secret": false, "client_settable": false, "description": "" },
    { "name": "crm_token", "type": "string", "required": true, "default": "", "secret": true, "client_settable": false, "description": "Used by the order_status tool" }
  ]
}
```

| Field | Type | Default | Description |
|---|---|---|---|
| `name` | string | — | `^[a-z][a-z0-9_]{0,31}$`. `sys` is reserved: `{{sys.date}}` and `{{sys.end_user.name}}` are built in. |
| `type` | `string` · `number` · `boolean` | `string` | Values are checked against it. |
| `required` | boolean | `false` | A missing required variable returns `422 variable_missing`; an undeclared one returns `422 variable_unknown`. |
| `default` | same as `type` | `""` | Used when no value is sent. Not allowed on secret variables. |
| `secret` | boolean | `false` | Secret values are accepted only per request (`ask`, `messages`), are never stored, logged or shown to the model, and can be used only in HTTP tool headers as `{{var.name}}`. |
| `client_settable` | boolean | `false` | Allow browsers (client tokens and the widget) to set this variable. Never for secrets. |
| `description` | string | `""` | For your team. |

Variables reach the model as clearly marked data, not as instructions.

## overrides

Overrides are **denied by default**. List the fields that may be changed for one `ask` call, or for a whole session with `POST /v1/sessions` and `PATCH /v1/sessions/{session}` (per-message overrides arrive in v1.1):

| Field | Type | Default | Description |
|---|---|---|---|
| `allowed` | array | `[]` | Any of `dialect`, `tone`, `instructions_append`, `temperature`, `model`, `tools_disable`, `history_limit` (up to the configured value). |
| `models` | array | `[]` | Models a request may switch to when `model` is allowed. |

Only secret keys can send overrides. `never_handle`, `escalation_reply`, tool grants and identity-bound tool access can never be overridden. Overriding anything that isn't listed returns `422 override_not_allowed`.

## fallback

| Field | Type | Default | Description |
|---|---|---|---|
| `message` | localized text | built-in default | What the customer sees when every model attempt fails. |
| `handoff_on_failure` | boolean | `true` | Also hand the session to your team after a failure. |

## widget

| Field | Type | Default | Description |
|---|---|---|---|
| `greeting` | localized text | built-in default | First message shown when the chat opens. |
| `launcher_label` | localized text | built-in default | Text on the launcher button. |
| `theme.accent` | color | `#0F766E` | Accent color of the widget. |
| `theme.position` | `start` · `end` | `end` | Corner of the launcher; follows the page direction (`end` is bottom-right in English, bottom-left in Arabic). |
| `anonymous_daily_conversations` | integer | `200` | Daily cap of anonymous widget conversations for this agent. |

The widget reads only `bot_name`, `language`, direction, `greeting`, `launcher_label` and `theme` from the **published** version.
