Source: https://cli.nylas.com/guides/ai-support-inbox-agent-account

# Build an AI Customer-Support Inbox

A support inbox is the textbook job for an AI agent: high volume, repetitive questions, and a clear escalation path. This guide builds one on a dedicated agent account. The agent owns a support address, reads new tickets as JSON, routes known senders with deterministic inbound rules, lets a model classify the rest, replies in-thread, escalates the hard cases to a human, and runs inside workspace guardrails that a malicious message can't talk its way past. One identity, one contained loop.

Written by [Hazik](https://cli.nylas.com/authors/hazik) Director of Product Management

Updated June 7, 2026

> **TL;DR:** A support agent on a dedicated agent account reads tickets with `nylas email list --unread --json`, routes known senders with inbound rules, replies in-thread with `email send --reply-to`, escalates hard cases to a human, and runs inside workspace rules a prompt injection can't disable.

## What is an AI support inbox?

An AI support inbox is an agent that owns a support email address and handles incoming tickets end to end: it reads each message, decides whether to answer, escalate, or drop it, and sends a reply on the same thread. Built on an agent account, the agent owns the address — tickets arrive at the agent's own inbox, and replies go out from the same identity, with no human mailbox in the loop.

Support is a strong fit for automation because the work is repetitive and bounded: a large share of tickets are password resets, status questions, and known issues that resolve from a template. The agent clears those and routes the genuinely hard cases to a person. The split between the model's judgment (which category is this?) and deterministic action (which reply, which rule) is what keeps the system predictable.

Support triage: a ticket is classified, then auto-replied, escalated to a human, or blocked as spamTicket arrivessupport@ inboxClassifyrules + modelAuto-replyin-thread, --reply-toEscalatestar + forward to humanBlock spaminbound rule

## Why run it on a dedicated agent account?

A support agent should own its own inbox, not operate inside a human's mailbox. On a dedicated agent account, every ticket and reply is attributable to the agent, the support queue stays separate from anyone's personal mail, and you can pause the whole operation by suspending one grant. That isolation is what makes an autonomous support agent safe to run unattended.

It also contains the risk. A support inbox is a textbook [lethal trifecta](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/) surface — private data, untrusted inbound content, and the ability to send external email all in one place. Because the agent account's rules live on the workspace, the agent can't prompt its way past a rule it does not control. The safety section below applies that containment to the support loop.

## How do I set up the support inbox?

Create the support identity with one command. The [`nylas agent account create`](https://cli.nylas.com/docs/commands/agent-account-create) call returns a grant in under 2 seconds, and the address is live for both inbound and outbound mail. Capture the grant ID so the triage loop runs non-interactively:

```bash
nylas agent account create support@yourapp.nylas.email
export NYLAS_GRANT_ID="$(nylas agent account get support@yourapp.nylas.email --quiet)"
```

Point your support form, docs, and product at this address. From here, every ticket lands in the agent's inbox and every reply leaves from the same grant — the customer sees one consistent sender, and you manage it all from the CLI.

## How does the agent triage incoming tickets?

Triage has two layers. Deterministic inbound rules on the workspace handle known patterns the moment a message arrives — before the agent loop even runs. The agent loop then classifies whatever is left. Start with rules for the obvious cases: a rule can star a VIP customer's mail so it's never auto-handled, and another can drop a known spam domain:

```bash
# Flag VIP customers for priority handling
nylas agent rule create \
  --name "Star VIP senders" \
  --trigger inbound \
  --condition from.domain,is,bigcustomer.com \
  --action mark_as_starred

# Drop a known spam domain before it reaches the queue
nylas agent rule create \
  --name "Block spam domain" \
  --trigger inbound \
  --condition from.domain,is,spammer.example \
  --action block
```

For everything else, the agent reads unread tickets and a model assigns a category. The `nylas email list --unread --json` call returns fresh messages with the fields a classifier needs — the sender, subject, snippet, and the message ID the reply will thread onto:

```bash
nylas email list --unread --json \
  | jq '.[] | {id, from: .from[0].email, subject, snippet}'
```

The model's job is classification only: password reset, billing, bug report, or escalate. The reply text and the routing decision come from your code, so a misclassification produces the wrong template, never an unbounded action. For the full classifier pattern, see [Build an AI Email Triage Agent](https://cli.nylas.com/guides/build-ai-email-triage-agent).

## How does the agent reply in-thread?

A support reply has to land on the original thread, not arrive as a fresh email. The `nylas email send` command threads the reply when you pass `--reply-to` with the original message ID — the same ID the triage step extracted with [jq](https://jqlang.github.io/jq/manual/). The sender is the support grant, so the customer sees a continuous conversation from one address:

```bash
# Capture the ticket once so both fields come from the same message
ticket=$(nylas email list --unread --json | jq -re '.[0]')
msg_id=$(echo "$ticket" | jq -re '.id')
sender=$(echo "$ticket" | jq -re '.from[0].email')

nylas email send \
  --to "$sender" \
  --reply-to "$msg_id" \
  --subject "Re: your request" \
  --body "Thanks for reaching out — here are the steps to reset your password..."
```

Reusable answers belong in a template rather than a hard-coded string. Pass `--template-id` with `--template-data` to merge the customer's name and ticket details into a stored template, which keeps tone consistent across thousands of replies. The [email templates guide](https://cli.nylas.com/guides/email-templates-cli) covers the template workflow.

## How does the agent escalate to a human?

The cases the agent can't resolve need a clean handoff. When the classifier returns "escalate," the agent forwards the ticket to the human queue with a short summary and leaves the original unread for a person to pick up, rather than guessing at an answer. Escalation is a feature, not a failure — a support agent that knows its limits is safer than one that always replies:

```bash
# Forward the ticket to the human support queue with context
nylas email send \
  --to humans@yourcompany.com \
  --subject "Escalation: billing dispute (ticket from $sender)" \
  --body "The agent could not resolve this. Original message ID: $msg_id. Summary: customer disputes a duplicate charge."
```

Leave the original ticket unread or starred so a human sees it in context. The model decides what to escalate; the forward itself is deterministic code. For an approval-gated variant where a human signs off before any customer reply goes out, see [Build a Human-in-the-Loop Email Agent](https://cli.nylas.com/guides/build-human-in-loop-email-agent).

## How do I run the triage loop?

The full loop is a script a cron job runs every minute. It reads the first unread ticket the CLI returns, classifies it, and either replies from a template or escalates. The model step is shown as a single classification call; everything around it is deterministic and fails loud under `set -euo pipefail`:

```bash
#!/usr/bin/env bash
set -euo pipefail

ticket=$(nylas email list --unread --json | jq -r '.[0] // empty')
[ -z "$ticket" ] && exit 0

msg_id=$(echo "$ticket" | jq -re '.id')
sender=$(echo "$ticket" | jq -re '.from[0].email')
body=$(echo "$ticket" | jq -r '.snippet')

# Model returns one of: reset | billing | bug | escalate
category=$(classify "$body")   # your LLM call

# Map each known category to a known template; anything else goes to a human.
case "$category" in
  reset)   tpl=tpl_reset ;;
  billing) tpl=tpl_billing ;;
  bug)     tpl=tpl_bug ;;
  *)
    # Unknown or "escalate" — never guess a template, hand it to a person.
    # Leave the ticket unread so a human still sees it in the queue.
    nylas email send --to humans@yourcompany.com \
      --subject "Escalation (ticket from $sender)" \
      --body "Needs a human. Category: $category. Message ID: $msg_id"
    exit 0 ;;
esac

nylas email send --to "$sender" --reply-to "$msg_id" \
  --subject "Re: your request" --template-id "$tpl"

# The ticket is answered — mark it read so the next run doesn't reprocess it
nylas email mark read "$msg_id"
```

Because the `case` statement maps each known category to a known template and routes everything else to a human, an unexpected classifier output escalates rather than firing a guessed template that doesn't exist. Answered tickets are marked read with `nylas email mark read` so the next run doesn't reply twice; escalated tickets stay unread for a human to pick up. Add audit logging around each branch so you can trace why the agent answered the way it did — the [audit guide](https://cli.nylas.com/guides/audit-ai-agent-activity) covers structured logging for agent actions.

## How do I keep the support agent safe?

A support inbox reads untrusted content all day, so containment is not optional. The defense is the agent account's workspace: outbound rules cap who the agent can email and a policy caps how much it can send, both evaluated before a message reaches the send pipeline. Containment lives outside the agent's decision loop, which is exactly why a prompt injection buried in a ticket can't switch it off.

```bash
# A support agent replies to many customer domains, so don't allow-list by domain —
# block the specific domains a malicious ticket might try to steer a reply toward.
nylas agent rule create \
  --name "Block flagged outbound domain" \
  --trigger outbound \
  --condition recipient.domain,is,exfil.example \
  --action block
```

A support agent has to email arbitrary customer domains, so a domain allow-list would block the very replies it's meant to send. Containment here is three layers instead: block specific known-bad recipient domains with rules, cap volume with a policy send limit so a runaway loop can't blast thousands of replies, and reconcile recipients against your data in the loop. The full trigger, condition, and action set is in [Agent Rules and Policies](https://cli.nylas.com/guides/agent-rules-and-policies), and the end-to-end containment pattern is in [Stop Your AI Agent From Going Rogue](https://cli.nylas.com/guides/stop-ai-agent-going-rogue).

## Next steps

- [Getting Started with Agent Accounts](https://cli.nylas.com/guides/getting-started-agent-accounts) — the architecture behind the support identity and its workspace
- [Build an AI Email Triage Agent](https://cli.nylas.com/guides/build-ai-email-triage-agent) — the classification layer in depth
- [Trigger Agents on Inbound Email with Webhooks](https://cli.nylas.com/guides/agent-account-webhooks) — replace this guide's cron loop with a real-time message.created trigger
- [Build an Invoice-Intake Agent](https://cli.nylas.com/guides/invoice-intake-agent-account) — the same read-classify-route loop for inbound invoices and attachments
- [Build an Order-Status Reply Agent](https://cli.nylas.com/guides/order-status-reply-agent-account) — a specialized support agent with an order-ownership authorization gate
- [Build a Human-in-the-Loop Email Agent](https://cli.nylas.com/guides/build-human-in-loop-email-agent) — require human sign-off before a reply goes out
- [Email Templates from the CLI](https://cli.nylas.com/guides/email-templates-cli) — store and merge reply templates for consistent tone
- [Stop Your AI Agent From Going Rogue](https://cli.nylas.com/guides/stop-ai-agent-going-rogue) — the containment rules that keep the agent inside its limits
- [Full command reference](https://cli.nylas.com/docs/commands) — every `nylas email` and `nylas agent` flag
- [Nylas v3 API documentation](https://developer.nylas.com/) — the API surface behind these commands
