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

# Integration guide

> Wire your own AI agent into Plain so it answers threads with its own identity and audit trail.

Integrate your own AI agent with Plain so it replies to customer threads, with its activity kept separate from your team's.

Plain also integrates with several third-party AI agent providers that can automatically resolve customer support threads on your behalf.

<Frame>
  <img src="https://mintcdn.com/plain/esnLSCL6AbjPxXFS/public/images/bring-your-own-agent.png?fit=max&auto=format&n=esnLSCL6AbjPxXFS&q=85&s=0fc66d0fb8983131fb55fa92541089e1" alt="Integration guide in Plain" width="1024" height="539" data-path="public/images/bring-your-own-agent.png" />
</Frame>

## Integration

Your agent handles threads like this. The next section covers the exact setup steps.

1. **Customer reaches out** → Plain creates a thread via your email, slack, chat or any other channel integration.

2. **Listen to webhooks to trigger your agent**

There are a few different webhooks you can use to start your agent flow, depending on your use case:

* `thread.thread_created` - Fired when a thread is created, if you you can listen to this webhook if you need logic to decide if to assign your agent

* `thread.thread_assignment_transitioned` - This is fired when a thread's assignment changes, this can be useful for when you want to use assignment to filter threads with your own logic, or via a Workflow rule.

* Channel events (`thread.email_received`, `thread.slack_message_received`, etc.)

3. **Example automation** flow:

* `thread.thread_created` webhook is received

  * Here you have the opportunity to handle all threads via your agent, or filter threads the Agent will handle based on some conditions (for example: you could check the thread's tier, and skip handling Enterprise Tier threads)

* Update thread agent status to In progress (see below for more info) -

  * `updateThreadAgentStatus(IN_PROGRESS)`

* Call your agent, with the context from the newly created thread

* **If agent can reply**:

  * `replyToThread`

  * `updateThreadAgentStatus(HANDLED)`

  * `markAsDone`

* **If the agent can't reply**

  * `replyToThread` with a message you will hand off

  * `updateThreadAgentStatus(HANDED_OFF)`

* **Error handling**:

  * A typical flow:

  * `createNote` with what happened

  * `updateThreadAgentStatus(HANDED_OFF)`

  * `unassignThread`

  * `markThreadAsTodo` this will mean the thread will go back into the Todo queues for a human to pickup

## Setup

### Create a machine user

First, create a machine user to represent your AI agent. You can do this from Settings > Machine users & API keys. You can also set a custom avatar to distinguish your agent.

### Subscribe to webhooks

Listen for the events that should make your agent act. At a minimum, subscribe to the following, depending on which channels you have enabled:

* `thread.thread_created`

* `thread.thread_status_transitioned`

* Channel events (for message bodies): `thread.email_received`, `thread.slack_message_received`, `thread.chat_received`, etc.

  Complete details can be found in the [webhook events documentation](/docs/webhooks/thread-created).

### Responding to threads

Your agent can respond to threads using standard reply mutations for the required channel. For channel agnostic thread replies, you can use the `replyToThread` mutation:

```graphql theme={null}
mutation ReplyToThread($input: ReplyToThreadInput!) {
  replyToThread(input: $input) {
    chat {
      id
      text
    }
    error {
      message
      code
    }
  }
}

# Input
{
  "input": {
    "threadId": "th_01H8H46YPB2S4MAJM382FG9423",
    "textContent": "The plain text version of your reply goes here.",
    "markdownContent": "The markdown **version** of your _reply_ goes here."
  }
}
```

Complete mutation reference can be found [here](/docs/graphql/messaging/reply-to-thread#graphql).

### Managing agent status

An important step is managing the Agent Status of a thread. This should be done as part of your integration, so that in Plain, we can display threads in the correct views.

Agent status has 3 states, defined below.

| Status        | When to use                                                                                                                                                                                                                                                 |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `IN_PROGRESS` | Your agent is actively working on a thread.                                                                                                                                                                                                                 |
| `HANDLED`     | Your agent has successfully resolved the thread.<br />You should [mark the thread as done](/docs/graphql/threads/status-changes#mark-thread-as-done).                                                                                                            |
| `HANDED_OFF`  | Your agent needs human assistance or couldn't resolve the issue.<br />Make sure to also [unassign the thread](/docs/graphql/threads/assignment#unassigning-threads), and optionally [move it back to TODO](/docs/graphql/threads/status-changes#mark-thread-as-todo). |

Only threads that have an agent status of `HANDED_OFF` will appear in your First Response, Next Response and Investigating queues.

This is intentional so that work your agent is handling is kept out of view, and only threads that need human interaction are visible.

To update Agent status you can use the API like so:

```graphql theme={null}
mutation UpdateThreadAgentStatus($input: UpdateThreadAgentStatusInput!) {
  updateThreadAgentStatus(input: $input) {
    thread {
      id
      agentStatus
      agentStatusDetail {
        type
        ... on AgentStatusDetailHandedOff {
          reason
        }
      }
      agentStatusUpdatedAt
    }
    error {
      message
      code
    }
  }
}

# Input
{
  "input": {
    "threadId": "th_01J9X3Z8M0ABCDEFGHIJK",
    "agentStatus": "IN_PROGRESS"
  }
}
```

As Plain keeps threads actively being handled by your agent out of the Todo queues you can use the Agent activity view to see all threads where an agent status has been set.

This can be found by going to **Plain → AI → Agent Activity.**

## Caveats

### Automatic handoff behavior

When a human agent replies to a thread that your AI marked as `HANDLED` or `IN_PROGRESS`, Plain automatically moves it to `HANDED_OFF` status. This signals collaboration between your AI and human agents.

**Why this matters**: If your AI responds to a thread but the customer replies again, and then a human agent jumps in, the thread will now appear in your Todo queues.
