# AI Reply

Hand this one turn to the thread's AI agent — the same generation and confidence gate as an inbound auto-reply — then continue the flow with what it decided.

## Getting here

**Automate → Workflows**, open a workflow, click **+** on the canvas, then pick **AI Reply** under **Add Steps**.

## Before you start

- An **owner**, **admin** or **agent** can add this step and save the workflow. Publishing that workflow — the thing that makes it run on live conversations — is an **owner** or **admin** action.
- Building and running workflows depends on your plan; see [pricing](/pricing).
- An AI agent set up for this workspace. The step answers as whichever agent is currently assigned to the thread, or the workspace's default agent if none is.

## Set up

1. Open the workflow and click **+** where the AI should answer.
2. Pick **AI Reply**.
3. Optional: write an **instruction** — extra direction for this one step, added on top of the agent's own persona, e.g. "keep it to two sentences".
4. Add a step after it for the path where the AI did not answer — an **Escalate** or an **Assign To (human)**, or a **Condition** on **AI sent a reply**.
5. Save the step, then publish the workflow.

## Fields

| Field | What it means | Default |
| --- | --- | --- |
| AI instruction | Extra direction for this step only, appended after the agent's own persona — it does not replace the agent's setup, knowledge base or guardrails. | empty |

## How it works

- Before generating anything, the step checks the AI kill switches in order: platform-wide, then this workspace's own AI switch, then the channel's — the same order and the same switches an inbound auto-reply checks. Any one of them being on fails the step with no message sent and no model call made.
- It then checks thread-level holds: a **Pause AI / Unassign** step earlier in this or another workflow, or a person already holding the thread (a teammate took it over, or the customer asked for one) both fail the step the same way — the AI does not speak on either, no matter what a workflow's own steps are otherwise allowed to send there.
- It answers as whichever AI agent is currently assigned to this thread (by **Assign To (AI)**, or automatic routing), falling back to the workspace's default agent only when none is assigned — so a thread that already belongs to a specific agent keeps that agent's voice and knowledge base rather than switching to the default mid-conversation.
- It reuses the exact generation and confidence-gate pipeline an inbound message's own auto-reply uses — the same retrieval, grounding and metering — rather than a separate model call, so token usage and guardrails are identical either way.
- Generation is metered (tokens and cost recorded) whether or not the reply ends up being sent — the spend already happened once the model answered.
- What the model decided is written to `ai.action` (`auto_reply` / `suggest` / `escalate`), `ai.confidence` and `ai.replied` (whether a reply actually went out) — readable by a **Condition** step placed after this one.
- Only a decision of `auto_reply` with non-empty text is sent; everything else — `suggest`, `escalate`, or an `auto_reply` with no text — is treated as the AI not answering (see When it fails), and `ai.replied` is left false.
- If the agent currently answering has its own **Auto-reply** toggle off, the decision is `suggest` whenever it would otherwise have qualified to send — but a message the model reads as sensitive or an explicit request for a human still decides `escalate` regardless of the toggle, and a confidence below the platform's hard escalate floor does too. Either way this step never sends on that agent.
- A sent reply still goes through the ordinary send path, so it needs the 24-hour messaging window to be open on a windowed channel the same as **Send a Message** does, and it is recorded under the agent's own name, not the workflow's.
- If the customer sends another message while the step is generating or about to send, the step stops rather than answering a question that is no longer the latest one — the run halts on this step instead of continuing with a stale answer.

## When it fails

- Before generation ever starts, several checks can fail the step outright, and NONE of them attempt to notify a human — a kill switch on (platform, workspace or channel), the thread paused by **Pause AI / Unassign** or held by a person, no customer message to answer (a booking, an order, a scheduled trigger started the run), or the agent could not be loaded. Each fails with its own reason and nothing is generated.
- Generation itself throws (a model or infrastructure error): the step fails with **AI generation failed**, again with no notify attempt.
- The model chose **suggest** or **escalate** — a genuine decline, AFTER generation completed: the step fails, `ai.replied` stays false, with the reason **AI did not answer (escalate, confidence 0.20)** (or `suggest`, with the model's real confidence), and THIS is the case that tries to notify a human. The push notification is sent every time this happens, with no deduplication; the in-app notification row IS deduplicated whenever `answer.asked_at` is already set on the run — which happens after any earlier **Ask a Question** step in the same run, not only when this step is itself retrying that question's own failure arm.
- The model returned an **auto_reply** with empty text (the generation pipeline produced a decision but no usable words): the reason is **AI returned an empty reply (auto_reply, confidence 0.85)** — a distinct reason so this is never confused with a deliberate decline. The same notify attempt runs as for a decline.
- The human-notification attempt itself can under-deliver, but not simply because the push failed: the in-app row landing is enough to count as "paged" even if the push reached nobody. The reason only gains **— human page failed** on a decline (never on an empty `auto_reply`), and only when the push reached nobody AND no in-app row landed (no assignee or workspace admin to write one for, or the row write failed). Either one landing counts as paged.
- The customer's message changed — checked both right before generation starts and again right after it finishes: either way, the step fails (no notify attempt either) and the run **stops entirely at this step** — later steps in the same run are not attempted on top of a reply that would have answered the wrong question.
- None of the failures above (except the stale-message one) stop the rest of the run on their own — the flow continues to the next step, on a thread nothing was sent to, unless you place an **Escalate** or **Assign** step (or a **Condition** on **AI sent a reply**) right after it.
- The reason for any of the above is in the workflow's **Activity** tab, under **Execution log**, on this step's line.

## Limits

- It answers as the AGENT currently on the thread, not necessarily the workspace's named default — an earlier **Assign To (AI)** step changes who answers here.
- The **instruction** field adds to the agent's persona for this one step; it cannot override the agent's knowledge base, guardrails or the confidence gate itself.
- A low-confidence or declined answer does not retry inside this step — it fails once, tries to notify a human, and moves on. A retry needs another **AI Reply** step reached some other way (e.g. after a customer rephrases).
- The human-notification push has no deduplication of its own — every declined run of this step sends one, even if the last one was seconds ago. The in-app notification row IS deduplicated, keyed off `answer.asked_at` — set by any earlier **Ask a Question** step in the same run, not only one this exact step is retrying on behalf of.
- An agent whose **Auto-reply** toggle is off never sends through this step. The decision is usually `suggest`, but a message the model reads as sensitive or a direct request for a human, or confidence below the platform's hard floor, still decides `escalate` regardless of the toggle.
- Most failures on this step — every kill switch, both thread holds, no customer message, an agent that could not load, and a stale customer message — notify nobody at all: no push, no in-app row. Only a genuine decline (`suggest`/`escalate`) or an empty `auto_reply` tries to notify, and "tried" is not "succeeded": the push alone reaching nobody does not on its own mark the attempt as failed — the in-app row landing is enough.
- It does not pause the run to wait for anything — the step already generated and, if it qualified, sent its one reply by the time the run continues.
- It does not stop the AI from answering on other, unrelated workflows or on plain inbound messages to this thread — only the specific kill switches and holds listed in How it works silence it, and only for the duration they are set.

## Best practices

- Always follow it with a human fallback on the path where the AI did not answer — **Escalate** or **Assign To (human)** — rather than relying on the step's own human-notification: most of this step's failures never attempt it at all (every kill switch, both holds, no customer message, an unloadable agent, a stale message), so it is a best-effort page on the decline path only, not a guarantee someone was told about every way this step can fail.
- Use a **Condition** on **AI sent a reply** rather than assuming success when you need the flow to branch differently depending on whether the AI actually spoke.
- Keep the **instruction** short and specific to this one turn — it is not the place to redefine the agent's whole personality for a single step.

## Use cases

- Let the AI answer a routine question mid-flow, with **Escalate** right after it on the path where it declined.
- Ask the AI to summarise or restate something in one sentence as a specific instruction, inside a flow that otherwise follows a fixed script.
- Branch on **AI sent a reply** to decide whether to close the conversation automatically or hand it to a teammate.

## FAQ and troubleshooting

### Does AI Reply check the same kill switches as a normal inbound reply?

Yes — platform, workspace, then channel, in that order, plus the thread-level holds (Pause AI / Unassign, or a person holding the thread). Any of them silences this step exactly the way it silences an inbound auto-reply.

### What happens if the AI isn't confident enough to answer?

The step fails, ai.replied stays false, and it tries to notify a human that the AI did not answer. Nothing is sent to the customer. The push side of that notification is not deduplicated — a workflow that keeps re-running this step pages fresh every time; the in-app row is deduplicated whenever an earlier Ask a Question step already set answer.asked_at on the run. Put Escalate or Assign after this step so someone follows up regardless.

### The agent I assigned has Auto-reply switched off. Does AI Reply still work?

It generates and evaluates the reply, but this step never sends on that agent. The decision is usually suggest, though a message the model reads as sensitive or a direct request for a human — or confidence below the platform's hard escalate floor — still decides escalate even with the toggle off. Either way, a human is who ends up notified, not the customer.

### Which AI agent answers if I've pinned this thread to a specific one?

Whichever agent is currently assigned to the thread — by an earlier Assign To (AI) step or automatic routing — not the workspace's default agent, unless none is assigned.

### Does the run continue if the AI didn't answer?

Yes, unless the customer sent a new message while this step was working, in which case the whole run stops here. Otherwise the flow moves to the next step regardless — which is why a human fallback right after this step matters.

## Related

- [Ask a Question](https://inchat.inseller.my/help/workflows/ask-a-question)
- [Escalate](https://inchat.inseller.my/help/workflows/escalate)
- [Assign To (AI)](https://inchat.inseller.my/help/workflows/assign-to-ai-agent)
- [Assign To (human)](https://inchat.inseller.my/help/workflows/assign-to-human)
- [Pause AI / Unassign](https://inchat.inseller.my/help/workflows/pause-ai-unassign)
- [Workflows: how a flow runs](https://inchat.inseller.my/help/workflows/workflows-overview)

---

Source files: `src/lib/workflows/ai-builder/spec.ts`, `src/lib/workflows/canvas-model.ts`, `src/components/workflows/WorkflowStepConfigDrawer.tsx`, `src/lib/workflows/engine.ts`, `src/lib/workflows/ai-reply-outcome.ts`, `src/lib/notifications/human-needed.ts`, `src/lib/push/notify.ts`, `src/lib/ai/agent.ts`, `src/lib/ai/workspace-kill-switch.ts`, `src/lib/ai/channel-kill-switch.ts`, `src/lib/ai/global-kill-switch.ts`, `src/lib/workflows/condition-fields.ts`, `src/lib/workflows/condition-catalog.ts`, `src/lib/inbox/wa-window.ts`, `src/lib/access/permissions.ts`, `.claude/rules/ai-sends.md`
