A given webhook event tells you something happened, but most agents only want to act on a subset of threads. This page covers the two patterns for deciding which threads your agent acts on, and how to check whether a given thread belongs to your agent.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.
Pattern 1: Listen broadly, filter in code
The simplest setup is to subscribe to one of the event types you care about (e.g.thread.thread_created, thread.email_received) and decide in your handler whether the agent should act.
Pattern 2: Assign the thread to the agent’s machine user (recommended)
A cleaner pattern, especially as your routing logic grows, is to assign threads to your machine user and have the agent run only on threads it’s been assigned. The “should the agent handle this?” decision lives in Plain, not in your code. This is the right pattern when you want your agent to handle support autonomously. Because the thread is actually assigned to the machine user, all of your existing reporting (volumes, resolution times, response times, and so on) attributes work done by the agent to the machine user in exactly the same way it would for a human teammate. Your webhook listens to events whose payload includes a thread, and checks whether that thread is assigned to your machine user. An example implementation could look like this:thread.thread_assignment_transitioned payload also includes previousThread (the state before the change), so you can inspect who the thread came from if you need to.
This pattern has some nice properties:
- No need to maintain filter logic in code
- Teammates can hand a thread to the agent by reassigning it, and the agent picks it up automatically.
- The agent can hand back by reassigning to a teammate (see change the assignee).
- You can change routing rules without redeploying. Just update the workflow in Plain.
Routing threads with a workflow
Plain’s workflows let you set up rules that act on threads automatically. A typical agent setup is a workflow that:- Triggers when a thread is created.
- Optionally checks conditions like channel, labels, customer tier, or support hours.
- Has an action that assigns the thread to your machine user.
Workflows are configured in the Plain UI, not via the API. Once a workflow assigns a thread to a machine user, your agent’s webhook receives a
thread.thread_assignment_transitioned event just like any other assignment change.Assigning programmatically
You can also assign threads to your machine user directly from code, for example from a classifier agent that picks which thread should go to which downstream agent. The mutation isassignThread, and AssignThreadInput accepts a machineUserId instead of a userId:
Avoiding accidental loops
Whichever pattern you use, if your agent both reacts to and writes to threads, watch out for loops:- Make sure your filters reject events your agent itself caused. The assignee filter above handles assignment changes; for message events, check the message’s author isn’t your own machine user before reacting.
- If the agent reassigns a thread to a human, the resulting
thread.thread_assignment_transitionedevent will have a different assignee onthread, so the assignee filter naturally drops it.

