> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs-parschat.parstechai.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs-parschat.parstechai.com/_mcp/server.

# Robot settings

> Read and change a robot’s settings: auto-close, handover, working hours, the AI and the AI operator.

A robot carries the same settings your customer sees in the ParsChat panel: when an idle conversation
closes, what a customer is told when a person takes over, the team's working hours, and how the AI
answers. Read them on every robot response, and change any of them with one call:

```bash
curl -X PATCH "https://api-chat.parstechai.com/v1/robots/rbt_8fK2mQ" \
  -H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"settings": {"conversations": {"auto_close": {"idle_minutes": 720}}}}'
```

The answer is the whole robot, with every setting as it now stands.

## Reading the settings

`settings` is on every robot you receive: `GET /robots/{robot_id}`, every item of `GET /robots`, and the
answers to `POST /robots` and `PATCH /robots/{robot_id}`.

**`Response`**

```json Response
{
  "id": "rbt_8fK2mQ",
  "name": "Rose Flower Shop",
  "external_id": "shop_10422",
  "description": "Flower shop on Instagram",
  "is_active": true,
  "channels": [{ "id": "chn_3pQ7xL", "type": "instagram", "is_active": true }],
  "created_at": "2026-09-20T11:04:33Z",
  "settings": {
    "conversations": {
      "auto_close": { "enabled": true, "idle_minutes": 360, "max_idle_minutes": 21600 },
      "operator_handover": { "message": "An operator will join you shortly.", "send_message": true },
      "offline": { "message": "Our team is offline right now; we will reply when we are back.", "send_message": true },
      "busy": { "message": "Our operators are busy; we will reply as soon as we can.", "send_message": false, "after_seconds": 300 },
      "offline_message": "Our team is offline right now; we will reply when we are back.",
      "ai_failure_message": null,
      "working_hours": {
        "enabled": true,
        "time_zone": "Asia/Tehran",
        "days": {
          "SAT": [{ "start": "09:00", "end": "18:00" }],
          "SUN": [{ "start": "09:00", "end": "13:00" }, { "start": "16:00", "end": "20:00" }],
          "MON": [{ "start": "09:00", "end": "18:00" }],
          "TUE": [{ "start": "09:00", "end": "18:00" }],
          "WED": [{ "start": "09:00", "end": "18:00" }],
          "THU": [{ "start": "09:00", "end": "13:00" }],
          "FRI": []
        }
      }
    },
    "ai": {
      "enhanced_accuracy": false,
      "creativity": 50,
      "language": "auto",
      "topic": "Flower shop: roses and tulips, delivery inside Tehran only"
    },
    "ai_operator": {
      "enabled": true,
      "display_name": "Rose assistant",
      "start_mode": "on_first_message",
      "availability": "always",
      "wait_minutes": 3
    }
  }
}
```

**A robot that never changed a setting shows the value actually in force**, not a placeholder. For
auto-close that is today's rule: `{"enabled": true, "idle_minutes": 10080}`, a week.

**`settings.ai` and `settings.ai_operator` are `null` for an account without an AI messages package.**
They need a plan that includes AI messages. A package whose messages are used up for this period still
counts: the settings do not lock and unlock as the balance runs down.

## Changing them

`PATCH /robots/{robot_id}` takes any subset of these fields:

* `name`, `external_id`, `description`, `is_active`
* `settings.conversations`, `settings.ai`, `settings.ai_operator`

The same `settings` object is accepted when you create a robot with `POST /robots`; anything you leave
out takes its default.

> **Note**
>
> **Only what you send changes, and nested objects merge.** Sending
> `{"settings": {"conversations": {"auto_close": {"idle_minutes": 720}}}}` keeps `enabled` as it was. A
> working-hours `days` object changes only the days it names.

Four rules hold for every request:

1. **All or nothing.** If any part of a request is refused, nothing in it is saved, not even the parts
   that were valid.
2. **An unknown field is refused** with `422`, never ignored, so a misspelt field cannot pass as saved.
3. **`null` is accepted only for `external_id` and `description`**, and clears them. Anywhere else it is
   `422`: leave a field out to keep it.
4. **An empty body `{}`** changes nothing and returns the robot.

The call needs the `robot:create` permission on your key, the same one that creates and deletes robots.

## Conversations

| Field                            | Type    | Allowed                                                                                                                                  | Default                            |
| -------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
| `auto_close.enabled`             | boolean |                                                                                                                                          | `true`                             |
| `auto_close.idle_minutes`        | integer | 15 to 21600 (15 days)                                                                                                                    | `10080` (7 days)                   |
| `auto_close.max_idle_minutes`    | integer | read-only, always 21600                                                                                                                  | `21600`                            |
| `operator_handover.message`      | string  | up to 500 characters; required while `send_message` is on                                                                                | ParsChat's default Persian message |
| `operator_handover.send_message` | boolean |                                                                                                                                          | `true`                             |
| `offline.message`                | string  | up to 500 characters; required while `send_message` is on                                                                                | ParsChat's default Persian message |
| `offline.send_message`           | boolean |                                                                                                                                          | `false`                            |
| `busy.message`                   | string  | up to 500 characters; required while `send_message` is on                                                                                | ParsChat's default Persian message |
| `busy.send_message`              | boolean |                                                                                                                                          | `false`                            |
| `busy.after_seconds`             | integer | 30 to 3600                                                                                                                               | `300`                              |
| `offline_message`                | string  | **deprecated**: the same text as `offline.message`; 1 to 500 characters, whitespace only restores the default                            |                                    |
| `ai_failure_message`             | string  | **deprecated and ignored**; always `null` in a response                                                                                  | `null`                             |
| `working_hours.enabled`          | boolean | `false` means every hour is a working hour                                                                                               | `false`                            |
| `working_hours.time_zone`        | string  | an IANA time zone, such as `Asia/Tehran`                                                                                                 | `UTC`                              |
| `working_hours.days`             | object  | keys `SAT` `SUN` `MON` `TUE` `WED` `THU` `FRI`; each a list of `{"start", "end"}` in `HH:MM`, `start` before `end`; `[]` closes that day | every day `[]`                     |

### When a conversation closes

A conversation with no message for its robot's `idle_minutes` is closed, and you receive a `chat`
event with `status: "closed"`. Closing runs hourly, so a conversation closes up to about an hour after
its limit.

* **`enabled: false` does not mean "never".** The conversation still closes after 15 days (21600
  minutes) without a message. `idle_minutes` is kept, so turning auto-close back on restores it.
* **A customer who writes again reopens the same conversation**: the same `conversation_id`, status
  `not_answered`, another `chat` event, and the AI starts from a fresh context.
* **The setting covers every channel ParsChat runs for the robot**: Instagram, Telegram, WhatsApp, GAP,
  the website widget and Direct API. Goftino and Crisp conversations follow those platforms' own
  lifecycle.

### What the customer is told while waiting for a person

A robot has three messages. Each is sent at most once per conversation.

| Message             | Sent when                                                                                                                                                 |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `operator_handover` | The AI hands the conversation to a person: it asks for one, the customer dislikes an answer, the customer sends a voice message, or the AI cannot answer. |
| `offline`           | The customer is waiting for a person while the robot is offline: outside its `working_hours`.                                                             |
| `busy`              | The customer is waiting for a person while the robot is online, and nobody has replied within `after_seconds`.                                            |

* **`operator_handover` is sent on every channel**, including Goftino and Crisp. It is sent only when
  the AI was answering: a conversation the AI did not answer (for example, when your AI messages are used
  up) is not "handed over", so it gets no `operator_handover` message.
* **`offline` and `busy` are sent on Instagram, Telegram, WhatsApp, GAP and the website widget.** Goftino
  and Crisp have their own settings for these, so they are not sent there.
* **A customer who is waiting** is one whose message the AI will not answer: the conversation is with
  your team, the AI is off, or it does not answer at this time (`ai_operator.availability`).
* **When the AI is set to answer after `wait_minutes`** (`start_mode: "if_operator_not_respond"`), the AI's
  answer replaces `busy`.
* A message switched on needs a text: `send_message: true` with an empty `message` is refused with `422`.

## AI

Needs an AI messages package.

| Field               | Type    | Allowed                                                              | Default |
| ------------------- | ------- | -------------------------------------------------------------------- | ------- |
| `enhanced_accuracy` | boolean | checks every answer before sending it; uses two AI units per message | `false` |
| `creativity`        | integer | 0 sticks to your customer's content, 100 is freest                   | `50`    |
| `language`          | string  | `auto` (the customer's own language), `persian`, `english`, `dutch`  | `auto`  |
| `topic`             | string  | what the robot answers about, given to the AI with every question    | `""`    |

`topic` is the panel's **Robot introduction**. It takes effect on the next customer
message: nothing is retrained and no characters are used. 5 characters or fewer counts as none.

## AI operator

Needs an AI messages package.

| Field          | Type    | Allowed                                                                                    | Default                         |
| -------------- | ------- | ------------------------------------------------------------------------------------------ | ------------------------------- |
| `enabled`      | boolean | `false`: the AI never answers                                                              | `true`                          |
| `display_name` | string  | 1 to 50 characters; the AI's name in the operator list                                     | ParsChat's default Persian name |
| `start_mode`   | string  | `on_first_message`, or `if_operator_not_respond` to wait `wait_minutes` for a person first | `on_first_message`              |
| `availability` | string  | `online`, `offline` or `always`: when, relative to the team being online, the AI answers   | `always`                        |
| `wait_minutes` | integer | 1 to 10080 (a week); used only by `if_operator_not_respond`                                | `3`                             |

## Deactivating a robot

`{"is_active": false}` is the panel's activation switch, and it is **not a pause**:

* Every channel of the robot is switched off (`is_active: false` on each channel), which frees their
  channel capacity. Only enabled channels count against your plan.
* The robot keeps its robot slot: robot capacity counts every robot that exists. Delete the robot to
  free its slot.
* `{"is_active": true}` reactivates the robot but **leaves its channels off**. Switch each back on with
  `PATCH /robots/{robot_id}/channels/{channel_id}`, which checks channel capacity as usual.

To stop only the AI answering while the channels stay live, set `settings.ai_operator.enabled` to `false`.

## Not in this API

* **The custom prompt.** A robot's prompt changes only through the panel's prompt assistant, where the
  AI rewrites the request, reports the parts it refused, and your customer accepts the result. It is
  not a field you can set here: `settings.ai.instructions` is refused as an unknown field.
* **The AI operator's avatar**, which is a file upload.
* **Plan controls**, such as message limits. They are set by ParsChat.

## Examples

### Change only the auto-close period

**`Request`**

```json Request
{ "settings": { "conversations": { "auto_close": { "idle_minutes": 720 } } } }
```

`200`, the full robot, with `"auto_close": {"enabled": true, "idle_minutes": 720, "max_idle_minutes": 21600}`.

### Turn auto-close off

**`Request`**

```json Request
{ "settings": { "conversations": { "auto_close": { "enabled": false } } } }
```

`200`, with `"auto_close": {"enabled": false, "idle_minutes": 720, "max_idle_minutes": 21600}`.
Conversations now close after 15 days without a message.

### Several settings at once

**`Request`**

```json Request
{
  "name": "Rose Flower Shop (Tehran)",
  "settings": {
    "conversations": {
      "operator_handover": { "send_message": true, "message": "Connecting you to a person." },
      "working_hours": {
        "enabled": true,
        "time_zone": "Asia/Tehran",
        "days": { "FRI": [{ "start": "10:00", "end": "14:00" }] }
      }
    },
    "ai_operator": { "start_mode": "if_operator_not_respond", "wait_minutes": 5, "availability": "offline" }
  }
}
```

`200`. Friday now opens 10:00 to 14:00 and the other six days are unchanged. The AI answers only
outside working hours, after 5 minutes without an operator reply, and a customer handed to a person is
told "Connecting you to a person."

### Tell waiting customers when the team is offline or busy

**`Request`**

```json Request
{
  "settings": {
    "conversations": {
      "offline": { "send_message": true, "message": "Our team is offline right now; we will reply when we are back." },
      "busy": { "send_message": true, "after_seconds": 120 }
    }
  }
}
```

`200`. A customer waiting outside working hours gets the offline message; one waiting for two minutes
without a reply during working hours gets the busy message, in ParsChat's default text.

### Change the topic

**`Request`**

```json Request
{ "settings": { "ai": { "topic": "Flower shop: roses, tulips and orchids, delivery across Iran" } } }
```

`200`, with the new `topic`, in effect from the next customer message. `"topic": ""` removes it.

### Clear your own id and description

**`Request`**

```json Request
{ "external_id": null, "description": null }
```

`200`, with `"external_id": null` and `"description": null`.

### Create a robot with settings

```bash
curl -X POST "https://api-chat.parstechai.com/v1/robots" \
  -H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name": "Orchid Lab", "external_id": "shop_20077",
       "settings": {"conversations": {"auto_close": {"enabled": true, "idle_minutes": 1440}}}}'
```

`201`, the full robot, with every setting you left out at its default.

## When a change is refused

Nothing in a refused request is saved.

| Request                                                                                | Answer                                                                                                                                      |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `{"settings": {"ai": {"creativity": 70}}}` on an account without an AI package         | `403 ai_not_in_plan`: "Your plan does not include AI messages, so AI settings cannot be changed."                                           |
| `{"settings": {"conversations": {"auto_close": {"idle_minutes": 30000}}}}`             | `422 validation_failed`: ``"`settings.conversations.auto_close.idle_minutes`: Input should be less than or equal to 21600"``                |
| `{"settings": {"conversations": {"auto_close": {"idle_minute": 720}}}}` (typo)         | `422 validation_failed`: ``"`settings.conversations.auto_close.idle_minute`: Extra inputs are not permitted"``                              |
| `{"settings": {"conversations": {"offline": {"send_message": true, "message": " "}}}}` | `422 validation_failed`: "A message that is switched on needs a text.", with `details.field` = `settings.conversations.offline.message`     |
| `{"settings": {"conversations": {"ai_failure_message": "shout"}}}`                     | `422 validation_failed`: ``"`settings.conversations.ai_failure_message`: Input should be 'offline_message', 'handover_message' or 'none'"`` |
| `{"settings": {"ai": {"creativity": null}}}`                                           | `422 validation_failed`: ``"`settings.ai.creativity`: Input should be a valid integer"``                                                    |
| `{"settings": {"ai": {"instructions": "…"}}}`                                          | `422 validation_failed`: the custom prompt is not part of this API                                                                          |
| `{"external_id": "shop_20077"}` when another of your robots uses it                    | `409 conflict`                                                                                                                              |
| A robot id that is not yours, or does not exist                                        | `404 not_found`, the same answer for both                                                                                                   |

The same `422` covers `creativity: 150`, `wait_minutes: 0`, `"start": "25:00"`, a window whose `start`
is not before its `end`, an unknown `time_zone`, `language: "french"` and `display_name: ""`. The message
names the field by its path; branch on `error.code`, not on the wording, which may change.