# How we run customer support with an AI agent

The support agent we run on Kortix — connected to Plain, our codebase, and Stripe. It triages and resolves inbound threads, and stops for human approval on anything sensitive.

Canonical page: https://kortix.com/use-cases/customer-support

We run our own customer support on Kortix with an AI agent connected to Plain
(our support tool), our codebase, and Stripe. Most support questions need
product knowledge, engineering context, and billing data at the same time: a
help-center chatbot can quote documentation but can't read the code behind a
bug, confirm whether a subscription renewed, or take a scoped action on an
account.

This write-up covers how the setup works: the connections, the session model,
and the guardrails.

- **Team:** Kortix — the company behind this platform
- **Support stack:** Plain.com
- **Connected systems:** Codebase · Stripe · Plain
- **Mode:** Trigger-driven · human-gated

## The problem

Support volume grows faster than the team. The questions that matter most are
the ones a canned macro can't answer: "why was I charged twice?", "this button
does nothing", "does your API support X?" Each one needs someone who understands
the product, can read the code, and can check the customer's account — usually a
senior engineer.

A help-desk bot handles the easy questions but not these. Hiring scales linearly
with tickets. And giving a generic AI assistant real access to production
systems is hard to justify in a security review.

## What we built

Our support inbox in **Plain** is connected to an agent running on Kortix. Every
inbound thread spawns its own isolated session with scoped access to the systems
a support issue can touch: the product's knowledge, the codebase, and Stripe. It
investigates, resolves what it can, and stops at a human for sensitive actions.

## How it works

### Connect Plain as the trigger

A signed webhook from Plain points at the project. Every new thread or customer
reply fires it, and each firing spawns a fresh **session** in its own isolated
sandbox. One customer, one thread, one sandbox. Sessions don't share state, and a
busy inbox means more sessions running in parallel.

### Give the agent the product's knowledge

Product knowledge lives as **skills** and **memory** loaded into every session:
how the product works, our support playbook, the reply tone, and resolutions that
worked before. The knowledge is updated as threads are resolved.

### Connect the systems an issue can touch

Through scoped **connectors**, brokered server-side so no raw token reaches the
model, the agent can:

- **Search the codebase** — for a reported bug, find the relevant code path,
check recent changes, and see whether it's already known.
- **Look up Stripe** — plan, invoices, and subscription state, to answer billing
questions from account data.
- **Read and reply in Plain** — full thread context in, a reply out.

### Set the guardrails

The agent is **read-mostly by default**: it investigates freely, but writes are
scoped. Refunds, plan changes, and anything touching a customer's money or
account stop at a **human approval gate**. Credentials are encrypted in the
secrets manager and injected at runtime, scoped to the agents you grant them to or written to
logs.

### Let each thread run

An inbound thread triages, investigates across the three systems, and either
resolves directly or hands off to a human with the work done and context
attached. "Why was I charged twice?" becomes a Stripe lookup with an answer.
"This button does nothing" becomes a codebase search that either explains the
behavior or files a bug report for engineering.

> **Summary**
> Connect the support platform via a **trigger**, give the agent scoped
> **connectors** into the systems an issue can touch, encode product knowledge as
> **skills** and **memory**, and gate sensitive actions behind a human. Each
> inbound thread then runs in its own session.

## Guardrails

Giving an agent access to the codebase, Stripe, and the support inbox is a
security question as much as a product one. The controls on Kortix:

- **Isolation.** Each thread runs in its own isolated sandbox on its own branch. A
  session can install, run, and experiment to reproduce a bug, and only what it's
  explicitly allowed to send is written back out.
- **Scoped secrets.** The Plain, Stripe, and GitHub credentials are encrypted in
  the secrets manager and injected into the sandbox at runtime, scoped to the agents you grant them to.
- **Human approval gates.** Irreversible actions (money, account state) require a
  person to approve.
- **Everything is code.** The agent's persona, skills, and permissions are files
  in the repo — versioned and changed through a reviewed **change request**
  rather than a dashboard setting.

## The outcome

- **24/7:** Coverage without a night shift
- **First-touch:** Many inbound threads resolved before a human opens them
- **3 systems:** Product knowledge, Stripe, and the codebase in one agent

The agent handles the investigation-heavy tickets that used to pull a senior
engineer off their work, and when it escalates, it escalates with the diagnosis
attached. Customers get an answer to the question at any hour.

The setup relies on four pieces: sandbox isolation to contain each session, a
secrets manager to broker tokens, human approval gates for irreversible actions,
and memory that improves as threads are resolved.
