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

# Overview

> Run up to 10 actions on a conversation in a single request.

<Info>
  **Endpoint** · `POST /conversation/actions`
</Info>

The `POST /conversation/actions` endpoint runs a **group of up to 10 actions**
on **a single conversation**, in the order you send them. Instead of making N
requests to label, leave a note, and run the AI, you chain them into a single
request. It's the recommended way to mutate a conversation from an external
client.

Each element of the `actions` array carries a `type` field. `send_message`
unifies the four send modes (text, media, quick reply, template).
`send_quick_reply_or_template` is the only one whose mode the server decides: it
sends the quick reply if the [24h window](/en/conversations) is open, or the
[template](/en/templates) if it's closed.

| Action                                                                           | What it does                                            | Triggers AI?          |
| -------------------------------------------------------------------------------- | ------------------------------------------------------- | --------------------- |
| [`send_message`](/en/action-groups/send-message)                                 | Sends a message: text, media, quick reply, or template  | No                    |
| [`send_quick_reply_or_template`](/en/action-groups/send-quick-reply-or-template) | Quick reply or template depending on the window         | No                    |
| [`assign_label`](/en/action-groups/assign-label)                                 | Assigns one or more labels                              | No                    |
| [`remove_label`](/en/action-groups/remove-label)                                 | Removes one or more labels                              | No                    |
| [`assign_mailbox`](/en/action-groups/assign-mailbox)                             | Moves the conversation to a mailbox or category         | No                    |
| [`context_note`](/en/action-groups/context-note)                                 | Leaves a context note                                   | No                    |
| [`mark_resolved`](/en/action-groups/conversation-status)                         | Marks the conversation as resolved                      | No                    |
| [`mark_pending`](/en/action-groups/conversation-status)                          | Marks the conversation as pending                       | No                    |
| [`run_ai`](/en/action-groups/run-ai)                                             | Runs the assigned AI employee (or an explicit one)      | Yes                   |
| [`ai_assistance`](/en/action-groups/ai-assistance)                               | Consults another AI employee for a suggested reply      | Yes                   |
| [`assign_ai_employee`](/en/action-groups/ai-employee)                            | Assigns an AI employee (with `run: true`, also runs it) | Only with `run: true` |
| [`unassign_ai`](/en/action-groups/ai-employee)                                   | Removes the AI employee (hands off to a human)          | No                    |
| [`cancel_schedule`](/en/action-groups/cancel-scheduled-message)                  | Cancels the active schedule: message or AI execution    | No                    |

<Note>
  A group allows up to **10 actions**, of which at most **3** can trigger the AI.
  Exceeding it fails with `BATCH_LIMIT_EXCEEDED`.
</Note>

Each action has its own page with its fields and an example — open the one you
need from the table or the nav. To send media, first upload it with the
[Upload media](/en/action-groups/upload-media) flow.

## Identify the conversation

The group runs on **one** conversation: you identify it with the `conversation`
object, which carries **exactly one** of `uuid`, `whatsapp_user_id`, `username`,
or `phone`, **plus `channel` (required)**.

<ParamField body="uuid" type="string">
  Stable key of a conversation you already know. The most direct path.
</ParamField>

<ParamField body="whatsapp_user_id" type="string">
  The contact's BSUID (Meta's opaque identity). Resolves by exact match;
  recommended identifier going forward.
</ParamField>

<ParamField body="username" type="string">
  Public WhatsApp username, with or without `@` and case-insensitive. If several
  contacts share it, the API returns `409` (`USERNAME_AMBIGUOUS`).
</ParamField>

<ParamField body="phone" type="string">
  Phone in E.164 format. If it is duplicated across contacts, the API returns
  `409` (`PHONE_NUMBER_AMBIGUOUS`) instead of guessing.
</ParamField>

<ParamField body="channel" type="string" required>
  The channel's public key — copy it from the dashboard under **Settings → Channels**
  (the **Channel key** field), or list them with `GET /channels`. **Required** in
  every request: it sets the target channel. On the contact-based paths
  (`phone`, `username`, `whatsapp_user_id`) it restricts resolution to that
  channel; with `uuid` the engine ignores it, but the contract still requires it.
  A key that does not exist → `404` (`CHANNEL_NOT_FOUND`).
</ParamField>

Full reference (resolution order, conversation merging, and contact
ambiguities) in [Identify a conversation](/en/conversations).

## How it runs

<Steps>
  <Step title="Synchronous validation">
    The API validates the request. If the preflight rejects it (e.g. the AI has
    no tokens), it returns a synchronous `4xx` and **no** action runs.
  </Step>

  <Step title="202 Accepted">
    If validation passes, it returns **`202`** immediately; the group runs in
    the **background**.
  </Step>

  <Step title="Execution in order">
    The actions run one by one, in the order sent.
  </Step>
</Steps>

<Warning>
  The `202` confirms the group was **accepted**, not that every action succeeded.
  The per-action result **does not travel in the response** — query it in the
  conversation status or in the business's activity log.
</Warning>

## Stop on error

`stop_on_error` controls what happens when an action fails during execution:

| Value            | Behavior                                                        |
| ---------------- | --------------------------------------------------------------- |
| `true` (default) | The first failure **halts** the remaining actions.              |
| `false`          | The group runs **all** actions, ignoring intermediate failures. |

## Escalation to pending

`escalate_on_error` controls whether a group failure escalates the conversation to `inbox_status='pending'` for a human to review:

| Value            | Behavior                                                                                                                                                                                                                                                                                                                                                                  |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `true` (default) | If an action fails with a **delivery, AI execution, DB write, or AI assignment race** error (`SEND_FAILED`, `PREPARE_FAILED`, `AI_EXECUTION_FAILED`, `ACTIONS_DEPTH_EXCEEDED`, `UPDATE_FAILED`, `NO_AI_EMPLOYEE_ASSIGNED`), the conversation is marked `pending` with `pending_reason='action_set_failed'`. Only one escalation per group, even if multiple actions fail. |
| `false`          | Failures are reported in the activity log but the conversation is left untouched.                                                                                                                                                                                                                                                                                         |

<Note>
  Integrator input errors (`*_NOT_FOUND`, `*_AMBIGUOUS`, `WINDOW_CLOSED`, `TEMPLATE_NOT_APPROVED`, etc.), billing guards (`WALLET_BLOCKED`, `VISION_WALLET_BLOCKED`), and the `mark_resolved` integrity guard (`TRANSCRIPTION_REQUIRED_TO_RESOLVE`) **never escalate** — fix them and retry without human intervention.
</Note>

## Assembling an action list

Each action is an object in the `actions` array, identified by its `type`. A
request groups several actions on the **same** conversation, run in the order
sent. The `conversation` object always includes `channel`. Example — label,
leave a note, and run the AI, without halting on failures:

<CodeGroup>
  ```jsonc Request theme={null}
  {
    "conversation": {
      "phone": "+5215512345678",
      "channel": "ch_7A9K2M4Q"
    },
    "stop_on_error": false,
    "actions": [
      { "type": "assign_label", "tags": [{ "name": "VIP" }] },
      { "type": "context_note", "note": "Customer requested a quote." },
      { "type": "run_ai", "ai_employee": { "name": "Agente Ventas" } }
    ]
  }
  ```

  ```jsonc 202 response theme={null}
  {
    "status": "processing",
    "actions_accepted": 3
  }
  ```
</CodeGroup>

### Identify by different fields

Change only the identifier inside `conversation` (`channel` is always included);
the rest of the request does not change:

<CodeGroup>
  ```jsonc By phone theme={null}
  {
    "conversation": {
      "phone": "+5215512345678",
      "channel": "ch_7A9K2M4Q"
    },
    "actions": [{ "type": "mark_resolved" }]
  }
  ```

  ```jsonc By BSUID theme={null}
  {
    "conversation": {
      "whatsapp_user_id": "5f8d0a2b1c9e",
      "channel": "ch_7A9K2M4Q"
    },
    "actions": [{ "type": "mark_resolved" }]
  }
  ```

  ```jsonc By username theme={null}
  {
    "conversation": {
      "username": "@mariana.lopez",
      "channel": "ch_7A9K2M4Q"
    },
    "actions": [{ "type": "mark_resolved" }]
  }
  ```

  ```jsonc By uuid theme={null}
  {
    "conversation": {
      "uuid": "7b8a1c2d-3e4f-5a6b-7c8d-9e0f1a2b3c4d",
      "channel": "ch_7A9K2M4Q"
    },
    "actions": [{ "type": "mark_resolved" }]
  }
  ```
</CodeGroup>

**Hand off to a human** — a single `unassign_ai` action, no fields. It's
idempotent: if the conversation already has no AI, it's a successful no-op.

<CodeGroup>
  ```jsonc Request theme={null}
  {
    "conversation": {
      "phone": "+5215512345678",
      "channel": "ch_7A9K2M4Q"
    },
    "actions": [{ "type": "unassign_ai" }]
  }
  ```

  ```jsonc 202 response theme={null}
  {
    "status": "processing",
    "actions_accepted": 1
  }
  ```
</CodeGroup>

<Tip>
  If the preflight rejects the group, the response is a synchronous `4xx` and no
  action runs. See [Errors](/en/errors).
</Tip>
