chatRoute()
Creates a chat route handler for streaming agent conversations in AI SDK format. The function registers an HTTP POST endpoint that accepts messages and executes an agent before streaming the response to the client in AI SDK-compatible format. Use it inside a custom API route.
Use handleChatStream() if you need a framework-agnostic handler.
chatRoute() keeps the existing AI SDK v5/default behavior. If your app is typed against AI SDK v6, pass version: 'v6'.
By default, chatRoute() forwards the incoming request's AbortSignal to agent.stream(). If the client disconnects, Mastra aborts the in-flight generation.
Set resumable: true to let the generation continue after a disconnect and allow AI SDK UI to reconnect to the active stream. Resumable chats don't support abort-based cancellation because useChat().stop() is indistinguishable from a refresh or network interruption at the server.
Usage exampleDirect link to Usage example
This example shows how to set up a chat route at the /chat endpoint that uses an agent with the ID weatherAgent.
import { Mastra } from '@mastra/core'
import { chatRoute } from '@mastra/ai-sdk'
export const mastra = new Mastra({
server: {
apiRoutes: [
chatRoute({
path: '/chat',
agent: 'weatherAgent',
}),
],
},
})
You can also use runtime-defined agent routing based on an agentId. The URL /chat/weatherAgent will resolve to the agent with the ID weatherAgent.
import { Mastra } from '@mastra/core'
import { chatRoute } from '@mastra/ai-sdk'
export const mastra = new Mastra({
server: {
apiRoutes: [
chatRoute({
path: '/chat/:agentId',
}),
],
},
})
ParametersDirect link to Parameters
version?:
'v5' for the existing default behavior. Pass 'v6' when your app is typed against AI SDK v6 response helpers. Pass 'v7' when your app is typed against AI SDK v7.path:
/chat or /chat/:agentId). Include :agentId for dynamic agent routing.agent?:
:agentId.agentVersion?:
{ versionId: '<id>' } to target an exact version, or { status: 'draft' } / { status: 'published' } to resolve by status. When the route is called, query parameters ?versionId=<id> or ?status=draft|published take precedence over this static value. Requires the Editor to be configured.resumable?:
GET <path>/:chatId/stream route, uses the AI SDK chat ID as the Mastra thread ID, and lets generation continue after the POST connection closes.defaultOptions?:
sendStart?:
sendFinish?:
sendReasoning?:
sendSources?:
includeSubAgentMetadata?:
data-tool-agent and data-tool-agent-step parts when the agent delegates to sub-agents. These parts can include sub-agent reasoning and tool calls and results. Set to false if your clients should not receive them.messageMetadata?:
traceId (undefined when tracing is disabled), so you can return it as metadata, for example ({ part, traceId }) => (part.type === 'start' ? { traceId } : undefined). See the AI SDK message metadata docs for details.heartbeatMs?:
Resumable streamsDirect link to Resumable streams
Set resumable: true on chatRoute() and resume: true on AI SDK UI's useChat() to reconnect after a refresh or network interruption. The route factory returns a two-item array containing:
POST <path>: Starts the agent run. The request's AI SDK chat ID becomes the Mastra memory thread ID.GET <path>/:chatId/stream: Replays cached events and follows the active run. The route returns204when the run is finished, unavailable, expired, or outside the caller's scope.
Pass the returned array as one entry in server.apiRoutes. Mastra flattens nested route arrays during server configuration.
import { Mastra } from '@mastra/core'
import { chatRoute } from '@mastra/ai-sdk'
export const mastra = new Mastra({
server: {
apiRoutes: [
chatRoute({
path: '/chat/:agentId',
resumable: true,
}),
],
},
})
Active-run discovery is process-local. A reconnect request must reach the same running server process that started the generation. Resumable mode doesn't forward the request AbortSignal, so useChat().stop() closes the client connection without cancelling the agent run.
If the chat uses a resource ID, set MASTRA_RESOURCE_ID_KEY in the request context through authentication middleware. Don't send the resource ID only as memory.resource in the POST body: the reconnecting GET request has no body, so chatRoute() rejects that configuration with 400.
Additional configurationDirect link to Additional configuration
You can use prepareSendMessagesRequest to customize the request sent to the chat route, for example to pass additional configuration to the agent:
const { error, status, sendMessage, messages, stop } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/chat',
prepareSendMessagesRequest({ messages }) {
return {
body: {
// With memory configured, send only the new message. Mastra loads the
// rest of the conversation from storage.
messages: [messages[messages.length - 1]],
// Pass memory config
memory: {
thread: 'user-1',
resource: 'user-1',
},
},
}
},
}),
})
Without memory, send the full messages array as the chat route uses it as the request context.
With memory, the stored thread is the source of truth, so regenerate and editing an earlier message in useChat don't change it. To change stored history, use memory.saveMessages or the memory store's updateMessages, then send a new message.