
discussion.threadId to tell you which thread the discussion is linked to.
Discussions can also be started from a workflow. In the workflow builder you can pick your agent as well.
High-level flow
- A user starts a discussion with your agent.
- Plain sends
discussion.message_createdto your webhook target. - Your agent sets the discussion status to
IN_PROGRESS, does its work, posts a reply, and setsIDLE. - Your agent can then log its tool calls as well as ask for a user’s approval. See tool calls for more info.
Permissions
For working with discussions, your machine user’s API key must have the following permissions:threadDiscussion:readandthreadDiscussion:editto read discussions, report status, request approvals, resolve or reopen discussionsthreadDiscussionMessage:createandthreadDiscussionMessage:editto post messages to the discussionthread:readandcustomer:readif the agent needs to read the linked thread
Webhooks
Subscribe your webhook target to these events:Decide whether to answer
discussion.message_created fires for every discussion in the workspace, including your own replies. Answer only when all of these are true:
Load your machine user once at startup and deduplicate on
message.id:
message.markdown is what the user wrote. message.workspaceFiles lists any files they attached. You must respond with a status code of 200 before your agent starts work so that Plain doesn’t retry the webhook delivery.
Replying
You can use thesendDiscussionMessage mutation to reply to the discussion using markdown. This also marks the discussion as having unread messages within the Plain app.
- SDK
- GraphQL
Updating the discussion status
Set the discussion’s agent status toIN_PROGRESS when a turn starts and IDLE when it ends. If the turn fails, post the error as a message first, then set IDLE.
- SDK
- GraphQL
Asking for a tool call approval will automatically update the discussion status to
TOOL_CALL_APPROVAL_PENDING.When a discussion is pending a tool call approval, you cannot change its status manually.Stop when asked
A user can stop your agent mid-turn. Plain sendsdiscussion.turn_stop_requested with the discussion to stop. Cancel the model call, post what you have if useful, and set the discussion status to IDLE. The discussion stays open for the next message.
Resolve the discussion
You can usechangeThreadDiscussionStatus to resolve a discussion when the user needs nothing further, or set the status to OPEN to reopen it.
- SDK
- GraphQL
Replying to customers on behalf of the user
A user in a discussion can ask your agent to message the customer. The agent can do this by calling thereplyToThread mutation.
By default the reply is from the machine user. To send it as the user who asked, add them to the API key’s impersonation allow list and pass impersonation.asUser. See reply as a user.
The allow list belongs to the API key, not the machine user. Open the API key from the machine user’s page and, under Impersonation, add who it may reply as:
- individual team members
- everyone holding a built-in role: Owner, Admin, or Support
- everyone holding a custom role

