Bring your own agent is currently in beta and free to use.
How a turn works
- A user opens Ask Sidekick, picks your agent, and sends a prompt.
- Plain sends
discussion.message_created. - Your agent reports
IN_PROGRESS, works, posts an answer, reportsIDLE. - For an action a user must decide, the agent asks for approval and waits on
discussion.tool_call_approval_resolved.
What you need
1
Create a custom agent
Create a machine user with Type set to Custom agent (on an existing one, Change type). That is what makes it appear in Ask Sidekick.On Add API key, expand the non-recommended groups and grant
threadDiscussion and threadDiscussionMessage:threadDiscussion:readandthreadDiscussion:edit: read, report status, ask for approvals, resolve or reopen. Status reporting needs both.threadDiscussionMessage:createandthreadDiscussionMessage:edit: post answers.upsertDiscussionToolCallalso needsthreadDiscussion:read.
thread:read and customer:read if the agent reads the thread the discussion is on.2
Subscribe to webhooks
Go to Settings → Webhooks, click Add webhook target, pin version
2026-09-06, and subscribe to discussion.*. Copy the signing secret from Settings → Request signing.3
Install the SDKs
@team-plain/graphql 3.0.0 or newer and @team-plain/webhooks 1.9.0 or newer.Decide whether to answer
Your own replies come back as webhooks. Answer only when all of these hold:
Call
myMachineUser once at startup. Deduplicate on message.id.
200 before you start working to avoid webhook retries.
Post an answer
sendDiscussionMessage posts Markdown as the machine user’s public name. That call is what marks the discussion unread.
- SDK
- GraphQL
error on the response.
Report what your agent is doing
updateDiscussionAgentStatus: IN_PROGRESS when the turn starts, IDLE when it ends (including after a failure: post the error as a message first).
- SDK
- GraphQL
You cannot set
TOOL_CALL_APPROVAL_PENDING or UNKNOWN. Asking for an approval sets pending. You cannot set IDLE or IN_PROGRESS while an approval is open.Report tool calls
upsertDiscussionToolCall is keyed by an id you choose. Report PENDING, then the same id as SUCCESS or ERROR.
- SDK
- GraphQL
toolCallId: yours, unique in the discussion, 1–256 characters[A-Za-z0-9_-]text: required every write, max 2000 characterserror: required onERROR, max 4000 charactersSUCCESSandERRORare final; a later write returnsresult: NOOP
Gate an action on a user
Report the call, then ask for approval. Plain shows a card withtext as the heading, justification underneath, and Approve / Deny.
- SDK
- GraphQL
toolCallId must already be PENDING. Asking again returns the same approval. The discussion moves to TOOL_CALL_APPROVAL_PENDING.
Then discussion.tool_call_approval_requested, and discussion.tool_call_approval_resolved when a user decides:
APPROVED: run the call, thenupsertDiscussionToolCallwith the outcome.DENIED: do not run it and do not reportERROR. Plain already failed the call withreviewerNote.- If you stop waiting: report
ERROR.
resolveDiscussionApproval is for your own review tooling. An agent’s API key is refused: it cannot approve itself.
- SDK
- GraphQL
Resolve the discussion
agentStatus is the turn. changeThreadDiscussionStatus opens or resolves the discussion.
- SDK
- GraphQL
OPEN to reopen. Resolve when the user needs nothing further.
Example and caveats
Two working agents on this API, one per repository:example-aisdk-assistant-agent on the Vercel AI SDK, and example-eve-assistant-agent on eve, Vercel’s agent framework.
- Keep one model session per discussion, not per message.
- Nothing posted in a discussion reaches the customer.

