Skip to main content
This guide is only for customers who had early access to the private alpha of the Notion Agent APIs. If you’re new to the APIs, start with the Quickstart. The public beta renames the alpha’s threads-and-messages routes to sessions and events, and adds routes for agent insights and controls.
Move off alpha routes by September 30, 2026If you use the alpha routes for listing agents, threads, messages, or chat, migrate to their public-beta replacements by that date. The new routes use JSON request bodies instead of query parameters and return sessions and events instead of threads and messages.

What’s changing

You only need to migrate the routes listed above. The public beta also adds optional endpoints to retrieve or cancel sessions, view Custom Agent metadata and insights, set credit limits, enable, disable, or delete Custom Agents, and make those changes in bulk. See the Overview for the full set.

Upgrade checklist

1
Replace GET /v1/agents with POST /v1/agents/query, moving your query parameters into a filter object.
2
Replace GET /v1/agents/:agent_id/threads with POST /v1/sessions/query, filtering on agent_id.
3
Replace GET /v1/threads/:thread_id/messages with POST /v1/sessions/:session_id/events/query, and read agent replies from agent.message events.
4
Replace POST /v1/agents/:agent_id/chat with POST /v1/sessions, moving agent_id into the request body.
5
Replace POST /v1/agents/:agent_id/chatStream with POST /v1/sessions and Accept: text/event-stream, and parse server-sent events instead of NDJSON lines.
6
Rename thread_id to session_id throughout your code, and handle the session statuses the alpha didn’t return: queued, in_progress, canceled, and terminated.

Step-by-step guide

Step 1: Query agents instead of listing them

Query agents is a POST that takes a JSON body. The alpha’s query parameters become filters, each naming a property and pairing it with a condition object, and name becomes the free-text query field. Combine several filters with and or or.
The response envelope is unchanged — results, has_more, and next_cursor — so your pagination loop keeps working. One thing to watch for in the results: fields you aren’t entitled to read come back as the string "hidden" rather than being omitted. credit_limit needs full access to the agent, and last_run_at needs edit access. See Query agents for the full filter, sort, and option list.

Step 2: Query sessions instead of threads

A thread is now a session. Query sessions is workspace-wide rather than nested under an agent, so the agent moves from the path into a filter.
The objects changed shape along with the name: A thread’s single pending status is now split into queued and in_progress, and cancellation and termination are reported separately from failure. Treat any status you don’t recognize as non-terminal rather than as an error.

Step 3: Read events instead of messages

Message history is now an event stream. Query session events returns one entry per thing that happened in the session — user input, agent output, thinking, tool calls, tool results, and status transitions — each with a monotonically increasing sequence.
Two consequences worth handling explicitly:
  • Agent replies are agent.message events, and their text lives in content parts rather than in a single string. Concatenate the text parts to reconstruct the reply.
  • Errors are no longer agent messages. The alpha surfaced inference and tool failures as role: "agent" messages whose content described the error; read the session’s error object and its session.status events instead.

Step 4: Create sessions instead of chatting

Create or update a session replaces the chat route. agent_id moves from the path into the body, and one route now covers all three things you do to a session: start it, add a message, and answer a required action.
Send session_id to continue an existing session, and send session_id with actions to approve or reject something the agent is waiting on. The response is a session object rather than a chat.invocation: Poll Retrieve a session for the result, which the alpha had no route for — you no longer have to re-list threads to find the one you just created.

Step 5: Switch streaming from NDJSON to server-sent events

POST /v1/agents/:agent_id/chatStream is replaced by the same POST /v1/sessions request with Accept: text/event-stream. The transport changes twice over: the alpha emitted newline-delimited JSON chunks that were session objects themselves, while the Public beta emits server-sent events whose data is an envelope naming what arrived.
Public beta
Switch on each envelope’s type rather than decoding data as a session event directly: A dropped connection no longer costs you the turn. Persist the id of the last event.committed.event you received, then reconnect with that exact ID as continue_from — along with the session_id — and the stream replays from there. continue_from takes a committed event’s ID, not its sequence and not a provisional or stream-control record, so anything else fails to resolve. See Create or update a session.