# Actions

> What your AI Agent can do during a conversation: the built-in Quickchat AI Actions, and Custom Actions that call your own API, an MCP server, or a shortcut like Google Sheets.

Define what your AI Agent can do during conversation. From answering questions using real-time API data, collecting leads and market research data, to performing custom tasks.

Quickchat AI Agents have two kinds of Action.

**Quickchat AI Actions** are built in, so you only switch them on and configure them:

- [Human Handoff](/ai-agent/actions/human-handoff/) passes the conversation to a person.
- [Smart Data Gathering](/ai-agent/actions/smart-data-gathering/) collects leads and other data during the chat.

**Custom Actions** reach outside Quickchat AI:

- **API Actions** call any HTTP endpoint. They are covered on this page.
- [MCP Actions](/ai-agent/actions/mcp-actions/) connect a whole MCP server at once.
- [Google Sheets](/ai-agent/google-sheets/) is a one-click shortcut that sets up a logging Action for you.

Every Action can read live conversation data through [AI Action Variables](/ai-agent/variables/), and the [Jira example](/channels/jira/) builds one end to end.

## Custom Actions

Custom Actions let your AI call external tools during a conversation — search internal systems, create tickets, trigger alerts, or fetch fresh data that isn't in the Knowledge Base. Find them in **Actions & MCPs** under the **Custom Actions** section.

Click **+ Add Action** to create one. The dropdown offers:

- **API Action** — call any REST endpoint over HTTP. The most general option; use this for anything that has a URL and accepts JSON.
- **MCP** — connect an external MCP server in one click and let the AI use the tools it exposes. See [MCP Actions](/ai-agent/actions/mcp-actions/).
- **HubSpot Action**: pre-built connection to your HubSpot CRM. Use it to work with contacts, deals, and tickets, including adding private ticket notes, without managing API tokens yourself.
- **Discord Action**: install editable support and moderation API Action templates. Requires your own Discord app. See [Discord Actions](#discord-actions).
- **Google Sheets** — read and write a spreadsheet you connect.
- **Shopify MCP** — connect a Shopify store via the Shopify Storefront MCP server so the AI can search your catalog and store policies, read product details, and build a cart. Only one Shopify MCP action per scenario. It does **not** cover order lookup — see the note below.

:::note
The **Remaining: N** counter next to the **+ Add Action** button shows how many actions your plan lets you keep active. Separately, every agent can hold at most 15 actions overall, regardless of plan. Upgrade your plan if you need more active actions, or [contact us](mailto:contact@quickchat.ai).
:::

:::caution[Order status is a separate action]
The Shopify Storefront MCP is customer-facing and exposes no order data, so an AI Agent connected only through it cannot look up a customer's order. Order status comes from a separate **Check order status** API Action that reads the Shopify Admin API, and it is set up automatically when you install the Quickchat app from the **Shopify App Store**. Stores connected by pasting a URL have no Admin access and therefore no order lookup — reinstall through the App Store if you need it.
:::

### How Custom Actions work

1. Choose an action type: API Action, MCP, HubSpot Action, Discord Action, Google Sheets, or Shopify MCP. Configure the connection and instructions the AI needs.
2. During a chat, the AI normally uses the action description and available tool or parameter details to decide when to run it. An API Action can instead run automatically before the first reply or when metadata conditions first match.
3. Quickchat executes the call. For an Action the AI chooses, or one that runs before the first reply, the AI can use the response. A metadata-triggered Action runs silently and its output is not added to the chat.
4. Test or connect the action from its editor before using it in conversations.

:::tip
Write the description as an instruction to the AI. Tell it **when** to use the action, **what** it returns, and any **guardrails** (e.g., "only call when the user provides an order number").
:::

### Create an API Action

1. Go to **Actions & MCPs**.
2. Click **+ Add Action** in **Custom Actions** and choose **API Action**.
3. Fill in **Details**:
   - **Name**: clear and descriptive.
   - **Description**: when to use it and what to include in parameters.
4. Under **When to run**, choose **AI decides**, **Before first reply**, or **When metadata matches**.
5. Configure **Connection**:
   - **Action Type**: HTTP method (GET, POST, etc.).
   - **Action endpoint URL**: full API URL.
   - **Headers**: add any required headers like `Authorization` or `content-type: application/json`.
6. Define **Parameters**: give each parameter a **name**, **location** (query, body, or header), and a **description** that tells the AI how to compose the value. Path values aren't a parameter location; insert them directly in the endpoint URL using `{{placeholder}}` templating.
7. **Test request** and verify the response. Then click **Done**.

:::tip
Any value field (URL, header, body, or query) accepts dynamic placeholders via the **+ Add AI Data** button. You can inject your AI parameters, built-in conversation variables (`{{scenario_id}}`, `{{language}}`, `{{country}}`, ...), and conversation metadata (`{{metadata_*}}`). See [AI Action Variables](/ai-agent/variables/) for the full reference.
:::

Beyond the request itself, an API Action has three optional settings worth knowing: **Save to memory**, **Run only when**, and **Response filter**.

### Choose when an API Action runs

Use **When to run** to select one of three modes:

The section is collapsed by default and shows the mode currently in force next to its heading; expand it to change the mode.

![The expanded When to run section showing the three modes: AI decides, Before first reply, and When metadata matches](https://docs.quickchat.ai/_astro/when-to-run-modes.Dw2pVxcD.png)

- **AI decides**: the default. The AI reads the Action description and decides whether the visitor's request needs the Action. Use this when the Action should run only for particular questions or after the AI collects information from the visitor.
- **Before first reply**: Quickchat runs the Action once after the visitor sends the first message and before the AI writes its first reply. The normal AI reply receives the response and any values saved with **Save to memory**. The AI can no longer choose this Action as a tool, so it runs on the first turn only and not again later in the conversation. Use this for customer identification, account context, entitlement checks, or other setup that every real conversation needs.
- **When metadata matches**: Quickchat checks the Action's **Run when** conditions after every AI turn and runs it the first time all conditions match. Values another Action saves to memory during that turn are available immediately. The AI cannot choose this Action as a tool, and its response is not added to the chat. Use it for internal notifications, CRM updates, or follow-up requests that should happen automatically when conversation data becomes available.

**Before first reply** is tied to the visitor's first message, not conversation creation. Opening a chat or receiving a welcome message does not run it, so abandoned chats make no request. Quickchat records the run before calling the endpoint, so it is not retried in the same conversation even if the request fails or its outcome is uncertain.

The first reply waits for the request, so this mode can add latency. If several Actions use this mode, they run one after another and their request times add up. If required metadata is missing, the endpoint is unavailable, or a request fails, the AI still replies. Open **Request log** on the Action to see what happened.

Because the Action runs before the AI can choose parameter values, every AI parameter it uses must have a default value. Metadata and built-in variables can be inserted directly into the URL, headers, query, or body.

**When metadata matches** requires at least one **Run when** condition and a default value for every AI parameter. Quickchat records the attempt before calling the endpoint, so once a request has been sent the Action is not retried in the same conversation, whether it succeeded, failed, or its outcome is uncertain. A request that could not be built at all, because a value it needs is still missing, is not recorded and stays eligible for a later turn. The Action runs right after the AI finishes its turn, so a failure never replaces the AI's reply. On channels that do not stream the reply, the request's round trip is added to the time the visitor waits for it.

Choosing **When metadata matches** reveals the **Run when** editor inside the same section, so the trigger and its conditions stay together.

![The When to run section set to When metadata matches, with two Run when conditions: escalation_flag is true and order_id exists](https://docs.quickchat.ai/_astro/when-metadata-matches-conditions.DIM2tl3U.png)

:::tip[Identify by email or phone]
For channels that may provide either an email address or a phone number, create one lookup Action for each identifier. Set both to **Before first reply**, then add a matching **Run only when** condition such as `visitor_email exists` or `visitor_phone_number exists`. Quickchat sends only the request whose metadata is present.
:::

### HubSpot Actions

**HubSpot Action** opens a gallery of pre-filled, editable API Actions that use your connected HubSpot account.

![The HubSpot CRM Actions gallery, with Add Note to Ticket under Tickets](https://docs.quickchat.ai/_astro/hubspot-add-note-template.BEFhSFrM.png)

The **Add Note to Ticket** template creates a private note on the HubSpot ticket linked to the conversation. Save the note content to conversation memory as `hubspot_note_body`, then add the template and activate it. It is preconfigured with **When metadata matches** and waits until both `hubspot_note_body` and Quickchat's server-provided `hubspot_ticket_id` are present. You can edit the `note_body` parameter's default value to add fixed text around `{{metadata_hubspot_note_body}}`.

The note is internal to HubSpot and is not shown to the visitor. The template attempts the request once per Quickchat conversation. Open the Action's **Request log** to confirm delivery or investigate a failed request.

### Discord Actions

**Discord Action** opens a gallery of pre-filled, editable API Actions:

- **Support & Tickets**: **Open Support Ticket** asks you to select a server and text channel. You can optionally select a mentionable support role. It creates three Actions that open a private thread, add the current requester, and post a summary with the optional role mention.
- **Moderation**: **Timeout Member**, **Kick Member**, **Ban Member**, **Unban Member**, **Assign Role**, **Set Slowmode**, and **Send Announcement**. Each template installs with one click and uses the server, member, role, or channel from the current Discord request.

Quickchat creates the HTTP method, URL, headers, body, parameters, description, response settings, and run conditions on the backend. The resulting cards are ordinary HTTP Request Actions. You can inspect and edit every field.

All seven moderation templates include `author_is_admin is true` under **Run only when**. Discord calculates the current author's Administrator permission, the Discord gateway supplies that value, and Quickchat checks it before sending the request. Public chat and API metadata cannot set this key. Test moderation Actions in Discord with both an administrator and a non-administrator account; AI Preview does not provide an authorized Discord sender.

:::caution
The moderation Actions are editable. Removing or weakening the `author_is_admin is true` condition removes their supplied authorization boundary. Kick and Ban do not ask for confirmation before executing.
:::

The templates add no automation of their own. The Agent runs them only when an administrator asks in the conversation; there is no strike counting across conversations, no verification of screenshots or other evidence, and no scheduled or self-initiated sanctions.

The bot itself still needs the Discord permission required by each request:

| Action | Bot permission or access |
|---|---|
| Timeout Member | Moderate Members |
| Kick Member | Kick Members |
| Ban Member, Unban Member | Ban Members |
| Assign Role | Manage Roles |
| Set Slowmode | Manage Channels |
| Send Announcement | View Channels and Send Messages |

Discord also enforces role hierarchy. Put the bot's role above members and roles it should manage. The template does not perform an additional hierarchy or per-permission check for the requesting administrator.

**Set Slowmode** and **Send Announcement** accept a channel ID parameter. Without an additional fixed target, the template does not verify that the channel belongs to the server where the administrator sent the request. If the same bot belongs to multiple servers, edit these Actions to use a fixed channel ID or separate the servers across Agents and bot credentials.

The Support Ticket installation uses existing Action features to pass data:

1. `open_support_ticket` creates a private thread in the selected channel, captures `$.id`, and saves it to conversation memory as `discord_ticket_thread_id`. It requires `discord_message is true`, runs only in the selected server, and runs only while that memory key does not exist.
2. `add_ticket_requester` reads `{{metadata_discord_ticket_thread_id}}` and the Discord-supplied `{{metadata_discord_author_id}}`. It requires `discord_message is true` and runs only in the selected server after the thread ID exists.
3. `post_in_ticket` reads the same saved thread ID, posts the Agent's one-line issue summary, and mentions the requester. If you selected a support role, it mentions that role too. It has the same Discord-message, server, and saved-thread conditions. Its Discord `allowed_mentions` body is limited to the requester and, when configured, that role.

The descriptions and your Agent prompt should tell the Agent to run those Actions in that order. There is no separate workflow state, lock, confirmation, retry, recovery, reconciliation, or replay-protection layer. If a result is uncertain, inspect Discord and the Action log before retrying because Discord might already have completed the request.

`discord_ticket_thread_id` is not cleared automatically. The starter therefore supports one ticket per Quickchat conversation. Start a new Discord conversation or thread for another ticket, or edit the Actions to add the memory lifecycle you need.

For Support Ticket, grant the bot **View Channels**, **Send Messages**, **Read Message History**, **Create Private Threads**, **Send Messages in Threads**, and **Manage Threads** in the selected ticket channel. Leave **Notify a role (optional)** set to **Don't notify a role** if no role should be mentioned. If you select a role, it must be mentionable and its members need access to the private threads. When no mentionable roles are available, tickets still open normally without a role notification. The gallery lists text channels and mentionable roles, but it does not validate all channel permission overrides.

:::caution
Discord Actions need the token from **your own Discord app** under **External Apps → Discord → Advanced setup**. The shared one-click bot alone cannot supply that token. See [Discord](/channels/discord/).
:::

The gallery is a setup shortcut, not an extra policy layer. If you edit a generated Action or create a custom Discord API Action, review its credentials, targets, parameters, response exposure, and Run only when conditions before enabling it.

### Save to memory

Capture a value from the API response and store it in the conversation's memory under a key you choose. Later Actions can reuse it as `{{metadata_<key>}}`, and it appears in the conversation details (Inbox, the API, and exports).

Reach for this when one Action produces something a later Action needs. A lookup Action can save a `customer_id` from its response; a follow-up Action then sends `{{metadata_customer_id}}` without the AI having to copy the value across.

In the **Save to memory** section of the API Action editor, give the captured value a **memory key** and point it at the part of the response you want to keep. From then on it's a metadata variable like any other.

Memory keys are shared with the Agent's Remote MCP tools. If an MCP tool already saves to the same key, the **memory key** field reports the clash when you save, so pick a different key.

:::tip
Use snake_case keys (letters, digits, underscores). A key like `customer_id` is referenceable as `{{metadata_customer_id}}`; keys with spaces or other punctuation can't be used in placeholders. See [Conversation metadata](/ai-agent/variables/#conversation-metadata) for how metadata variables work.
:::

![The Save to memory section of the API Action editor](https://docs.quickchat.ai/_astro/save-to-memory.dFSijW7g.png)

### Run only when

Restrict an Action so it runs **only when conditions on the conversation metadata hold**. Conditions are always checked on our side, outside the model. With **AI decides** or **Before first reply**, Quickchat checks them immediately before sending the request. With **When metadata matches**, the conditions themselves trigger the request after an AI turn, without waiting for the AI to choose the Action.

This is the right tool for privileged or irreversible Actions (banning a member, issuing a refund, deleting a record). A line in your prompt that says "only admins can do this" is a helpful instruction, but it isn't a security boundary: a determined user can argue with the model or attempt a prompt injection. A run-condition is deterministic and lives outside the prompt, so it holds regardless of what the conversation says.

- **The run-condition is the boundary.** It's evaluated server-side and isn't part of the prompt the model reads. This is what actually stops the Action from running.
- **The prompt rule is the user experience.** Keep a line in your prompt too, so the AI declines politely and explains why instead of going silent.

Add conditions in the **Run only when** section of the Action editor. This section is labelled **Run when** for a **When metadata matches** Action. Click **Add condition**, pick a metadata key (for example `telegram_sender_is_admin`), and choose how to compare it:

| Condition | Passes when the metadata value... |
|---|---|
| is true | is truthy |
| is false | is falsy |
| exists | is present on the conversation |
| does not exist | is absent |
| equals | matches a value you specify |
| does not equal | differs from a value you specify |

The Action runs only when **every** condition holds. A condition on a key that isn't set on the conversation doesn't pass. When a condition blocks an Action chosen by the AI, the AI can explain that the Action is unavailable. A **When metadata matches** Action remains silent until its conditions pass.

![The Run only when section with one condition: telegram_sender_is_admin is true](https://docs.quickchat.ai/_astro/run-conditions.DKtagFC9.png)

When a condition fails, no request is sent and the AI tells the user it can't do that. Below, the same ban request is blocked for a non-admin and allowed for an admin, with nothing changed but who is asking:

![A non-admin's ban attempt blocked by the run-condition: no request is sent and the result is a denial](https://docs.quickchat.ai/_astro/gate-blocked.CHrDQiPI.png)

![The same ban request from an admin: the run-condition passes and the Action returns 200](https://docs.quickchat.ai/_astro/gate-allowed.ClC6fDEQ.png)

:::caution
Only gate on metadata that **Quickchat sets** (such as a channel-verified `telegram_sender_is_admin` flag), not on values the visitor can influence. See [Conversation metadata](/ai-agent/variables/#conversation-metadata) for where each key comes from.
:::

### Response filter

By default the AI sees the **full API response**. Add [JSONPath](https://goessner.net/articles/JsonPath/) expressions in the **Response filter** section to limit the AI to specific parts of it.

Two common reasons to filter:

- **Hide sensitive fields.** Keep customer emails, payment details, or internal ids out of the model's context when the endpoint returns more than the AI needs.
- **Shrink the prompt.** Chatty APIs can return large payloads; filtering to the few fields that matter keeps the response small and the AI focused.

Add one or more JSONPath expressions and the AI receives only the matched parts. For example, `$.data.items[*].name` keeps just the item names from a larger response.

:::tip
Use the **Test Response** panel to see exactly what the AI will receive after filtering before you rely on it. See [Testing your Action](/ai-agent/variables/#testing-your-action).
:::

:::caution
A JSONPath that matches nothing leaves the AI with an empty response. Start without a filter, confirm the call works, then tighten it.
:::

![The Response filter section of the API Action editor](https://docs.quickchat.ai/_astro/response-filter.D6c9224p.png)

### Best practices

- **Be explicit in descriptions**. Tell the AI when to use the action and what each parameter should contain.
- **Keep scopes minimal**. Only include the headers and tokens the endpoint needs. For Jira, use the Basic header derived from your Atlassian email and API token.
- **Test before rollout** using the Test request panel. Confirm status codes and sample payloads look right in the target tool.

#### Tutorials

- [Connect an AI Agent to Jira tickets](/channels/jira/) (canonical reference) or the [narrated blog walkthrough](https://quickchat.ai/post/search-jira-tickets-in-ai-conversation)
- [Send Slack notification with an AI Action](https://quickchat.ai/post/slack-notification-ai-action)
