Server routes
Server adapters register these routes when you call server.init(). All routes are prefixed with the prefix option if configured.
AgentsDirect link to Agents
| Method | Path | Description |
|---|---|---|
GET | /api/agents | List all agents |
GET | /api/agents/:agentId | Get agent by ID (supports version query params) |
POST | /api/agents/:agentId/generate | Generate agent response |
POST | /api/agents/:agentId/stream | Stream agent response |
POST | /api/agents/:agentId/send-message | Send a user message to an active or idle thread |
POST | /api/agents/:agentId/queue-message | Queue a user message for the next thread turn |
POST | /api/agents/:agentId/signals | Send a lower-level signal to an active or idle thread |
POST | /api/agents/:agentId/threads/subscribe | Subscribe to a thread stream |
POST | /api/agents/:agentId/send-tool-approval | Approve or decline a tool call and resume through a thread subscription |
POST | /api/agents/:agentId/resume-stream | Resume a suspended agent stream with custom data |
GET | /api/agents/:agentId/tools | List agent tools |
POST | /api/agents/:agentId/tools/:toolId/execute | Execute agent tool |
Get agent query parametersDirect link to Get agent query parameters
GET /api/agents/:agentId accepts optional query parameters to control which stored config version is applied as overrides to code-defined agents:
| Parameter | Type | Default | Description |
|---|---|---|---|
status | 'draft' | 'published' | 'published' | Which stored version to resolve. draft returns the latest version, and published returns the active version. |
versionId | string | None | A specific version ID to resolve. Takes precedence over status. |
# Get agent with active published overrides (default)
GET /api/agents/my-agent
# Get agent with latest draft overrides
GET /api/agents/my-agent?status=draft
# Get agent with a specific version's overrides
GET /api/agents/my-agent?versionId=abc123
Generate request bodyDirect link to Generate request body
{
messages: CoreMessage[] | string; // Required
instructions?: string; // System instructions
system?: string; // System prompt
context?: CoreMessage[]; // Additional context
memory?: { key: string } | boolean; // Memory config
resourceId?: string; // Resource identifier
threadId?: string; // Thread identifier
runId?: string; // Run identifier
maxSteps?: number; // Max tool steps
activeTools?: string[]; // Tools to enable
toolChoice?: ToolChoice; // Tool selection mode
requestContext?: Record<string, unknown>; // Request context
output?: ZodSchema; // Structured output schema
}
Generate responseDirect link to Generate response
{
text: string;
toolCalls?: ToolCall[];
finishReason: string;
usage?: {
promptTokens: number;
completionTokens: number;
};
}
Agent message routesDirect link to Agent message routes
Use POST /api/agents/:agentId/send-message to send a user message to the active agent loop or wake an idle thread. Use POST /api/agents/:agentId/queue-message when the active run should finish before Mastra starts a follow-up run.
Both routes accept the same request body:
{
message: string | Array<TextPart | FilePart> | {
contents: string | Array<TextPart | FilePart>;
attributes?: Record<string, JSONValue>;
metadata?: Record<string, unknown>;
providerOptions?: ProviderMetadata;
};
runId?: string;
resourceId?: string;
threadId?: string;
ifActive?: {
behavior?: 'deliver' | 'persist' | 'discard';
attributes?: Record<string, string | number | boolean>;
};
ifIdle?: {
behavior?: 'wake' | 'persist' | 'discard';
streamOptions?: Omit<AgentExecutionOptions, 'messages'>;
attributes?: Record<string, string | number | boolean>;
};
}
When runId is omitted, resourceId and threadId are required. ifIdle only applies to thread-targeted requests, not run-targeted requests.
Send a messageDirect link to Send a message
curl -X POST http://localhost:4111/api/agents/supportAgent/send-message \
-H 'Content-Type: application/json' \
-d '{
"message": {
"contents": "Show the shorter version.",
"attributes": { "sentFrom": "web" }
},
"resourceId": "user_123",
"threadId": "thread_456"
}'
Queue a messageDirect link to Queue a message
curl -X POST http://localhost:4111/api/agents/supportAgent/queue-message \
-H 'Content-Type: application/json' \
-d '{
"message": "Also check whether the tests need updates.",
"resourceId": "user_123",
"threadId": "thread_456"
}'
Both routes return:
{
accepted: true;
runId: string;
signal?: CreatedAgentSignal;
}
Subscription tool approval routesDirect link to Subscription tool approval routes
Use POST /api/agents/:agentId/send-tool-approval when the client already has an active thread subscription. The route resumes the run and returns a JSON acknowledgement. Resumed stream chunks are delivered through POST /api/agents/:agentId/threads/subscribe.
The route accepts this request body:
{
resourceId: string;
threadId: string;
toolCallId: string;
approved: boolean;
requestContext?: Record<string, unknown>;
}
The route returns:
{
accepted: true;
runId: string;
toolCallId?: string;
}
WorkflowsDirect link to Workflows
| Method | Path | Description |
|---|---|---|
GET | /api/workflows | List all workflows |
GET | /api/workflows/run-counts | Get per-workflow counts of running and suspended runs |
GET | /api/workflows/:workflowId | Get workflow by ID |
POST | /api/workflows/:workflowId/create-run | Create a new workflow run |
POST | /api/workflows/:workflowId/start-async | Start workflow and await result |
POST | /api/workflows/:workflowId/stream | Stream workflow execution |
POST | /api/workflows/:workflowId/resume | Resume suspended workflow |
POST | /api/workflows/:workflowId/resume-async | Resume asynchronously |
GET | /api/workflows/:workflowId/runs | List workflow runs |
GET | /api/workflows/:workflowId/runs/:runId | Get specific run |
Run counts responseDirect link to Run counts response
The /api/workflows/run-counts endpoint returns counts of running and suspended runs for every registered workflow. The record is keyed by the workflow's registry key from the Mastra config, and the server may cache the response for a few seconds:
{
[workflowRegistryKey: string]: {
running: number;
suspended: number;
};
}
Dynamic workflowsDirect link to Dynamic workflows
Dynamic workflow definitions (beta) are workflows expressed as JSON, persisted through the workflowDefinitions storage domain, and live-registered on the running instance. See Dynamic workflows.
| Method | Path | Description |
|---|---|---|
GET | /api/stored/workflows | List dynamic workflow definitions, filterable by status and authorId |
GET | /api/stored/workflows/:dynamicWorkflowId | Get a dynamic workflow definition by ID |
POST | /api/stored/workflows | Upsert a definition (plus optional helper dependencies) and live-register it |
DELETE | /api/stored/workflows/:dynamicWorkflowId | Delete a dynamic workflow definition and unregister the live workflow |
On authenticated servers, the read routes require the stored-workflows:read permission and the write routes require stored-workflows:write. Registered dynamic workflows are executed through the ordinary /api/workflows/:workflowId routes above.
Create run request bodyDirect link to Create run request body
{
resourceId?: string; // Associate run with a resource (e.g., user ID)
disableScorers?: boolean; // Disable scorers for this run
}
Request body for /start-asyncDirect link to request-body-for-start-async
{
resourceId?: string; // Associate run with a resource (e.g., user ID)
inputData?: unknown;
initialState?: unknown;
requestContext?: Record<string, unknown>;
tracingOptions?: {
spanName?: string;
attributes?: Record<string, unknown>;
};
}
Stream workflow request bodyDirect link to Stream workflow request body
{
resourceId?: string; // Associate run with a resource (e.g., user ID)
inputData?: unknown;
initialState?: unknown;
requestContext?: Record<string, unknown>;
closeOnSuspend?: boolean;
}
Resume request bodyDirect link to Resume request body
{
step?: string | string[];
resumeData?: unknown;
requestContext?: Record<string, unknown>;
}
ToolsDirect link to Tools
| Method | Path | Description |
|---|---|---|
GET | /api/tools | List all tools |
GET | /api/tools/:toolId | Get tool by ID |
POST | /api/tools/:toolId/execute | Execute tool |
Execute tool request bodyDirect link to Execute tool request body
{
data: unknown; // Tool input data
requestContext?: Record<string, unknown>;
}
MemoryDirect link to Memory
| Method | Path | Description |
|---|---|---|
GET | /api/memory/threads | List threads |
GET | /api/memory/threads/:threadId | Get thread |
POST | /api/memory/threads | Create thread |
DELETE | /api/memory/threads/:threadId | Delete thread |
POST | /api/memory/threads/:threadId/clone | Clone thread |
GET | /api/memory/threads/:threadId/messages | Get thread messages |
POST | /api/memory/threads/:threadId/messages | Add message |
Create thread request bodyDirect link to Create thread request body
{
resourceId: string;
title?: string;
metadata?: Record<string, unknown>;
}
Clone thread request bodyDirect link to Clone thread request body
{
newThreadId?: string; // Custom ID for cloned thread
resourceId?: string; // Override resource ID
title?: string; // Custom title for clone
metadata?: Record<string, unknown>; // Additional metadata
options?: {
messageLimit?: number; // Max messages to clone
messageFilter?: {
startDate?: Date; // Clone messages after this date
endDate?: Date; // Clone messages before this date
messageIds?: string[]; // Clone specific messages
};
};
}
Clone thread responseDirect link to Clone thread response
{
thread: {
id: string;
resourceId: string;
title: string;
createdAt: Date;
updatedAt: Date;
metadata: {
clone: {
sourceThreadId: string;
clonedAt: Date;
lastMessageId?: string;
};
// ... other metadata
};
};
clonedMessages: MastraDBMessage[];
}
VectorsDirect link to Vectors
| Method | Path | Description |
|---|---|---|
POST | /api/vectors/:vectorName/upsert | Upsert vectors |
POST | /api/vectors/:vectorName/query | Query vectors |
POST | /api/vectors/:vectorName/delete | Delete vectors |
Upsert request bodyDirect link to Upsert request body
{
vectors: Array<{
id: string
values: number[]
metadata?: Record<string, unknown>
}>
}
Query request bodyDirect link to Query request body
{
vector: number[];
topK?: number;
filter?: Record<string, unknown>;
includeMetadata?: boolean;
}
Datasets and experimentsDirect link to Datasets and experiments
| Method | Path | Description |
|---|---|---|
GET | /api/datasets | List datasets |
POST | /api/datasets | Create a dataset |
GET | /api/datasets/:datasetId | Get dataset by ID |
PATCH | /api/datasets/:datasetId | Update a dataset |
DELETE | /api/datasets/:datasetId | Delete a dataset |
GET | /api/datasets/:datasetId/items | List dataset items |
POST | /api/datasets/:datasetId/items | Add a dataset item |
GET | /api/experiments | List experiments across datasets |
GET | /api/datasets/:datasetId/experiments | List experiments for a dataset |
POST | /api/datasets/:datasetId/experiments | Trigger an experiment, or create one without starting it (start: false) |
POST | /api/datasets/:datasetId/experiments/:experimentId/items/:itemId/run | Execute one experiment item server-side |
POST | /api/datasets/:datasetId/experiments/:experimentId/results | Submit an externally computed item result |
POST | /api/datasets/:datasetId/experiments/:experimentId/finalize | Finalize a caller-driven experiment |
GET | /api/datasets/:datasetId/experiments/:experimentId | Get experiment by ID |
GET | /api/datasets/:datasetId/experiments/:experimentId/results | List experiment results |
POST | /api/datasets/:datasetId/compare | Compare two experiments |
Caller-driven experiment routesDirect link to Caller-driven experiment routes
Use these routes when your own orchestrator (for example a Temporal workflow) owns the experiment loop and Mastra acts as the system of record. Item runs, result submission, and finalization are safe to retry. Creation is safe to retry only when the request includes a caller-supplied id; without one, each retry creates a new experiment. Item runs and result submissions upsert on (experimentId, itemId, attempt). Finalize returns the stored record if the experiment is already completed.
Create experiment request bodyDirect link to Create experiment request body
Post to /api/datasets/:datasetId/experiments with start: false to create the experiment without running it. Include targetType and targetId when Mastra should execute items via the run-item route. Omit both for pure ingestion via the results route.
{
start?: boolean; // false creates the experiment without running it. Defaults to true.
id?: string; // Caller-supplied experiment id (e.g. a workflow run id) for idempotent creates
targetType?: 'agent' | 'workflow' | 'scorer'; // Provide with targetId, or omit both
targetId?: string;
scorerIds?: string[]; // Run-level scorer ids, resolved server-side. Requires a target when start is false.
name?: string;
description?: string;
metadata?: Record<string, unknown>;
version?: number; // Pin to a specific dataset version (defaults to latest)
provenance?: {
source?: string;
sourceId?: string;
sourceVersion?: string;
metadata?: Record<string, unknown>;
};
grouping?: { // Stable grouping dimensions for comparisons and repeated trials
experimentSetId?: string;
comparisonId?: string;
variantId?: string;
trialIndex?: number;
};
}
Returns { experimentId, status, totalItems, datasetVersion } when start is false.
Run experiment item request bodyDirect link to Run experiment item request body
POST /api/datasets/:datasetId/experiments/:experimentId/items/:itemId/run executes the experiment's target against one item and runs the resolved scorers, then upserts the result. Requires an experiment created with a target.
{
attempt?: number; // Zero-based repetition index. Defaults to 0.
requestContext?: Record<string, unknown>; // Merged with the item's own request context (item wins)
}
Returns { result, scores } with the persisted result row and the scores produced for the item.
Submit experiment result request bodyDirect link to Submit experiment result request body
{
itemId: string; // Dataset item this result belongs to
attempt?: number; // Zero-based repetition index. Defaults to 0.
input?: unknown; // Defaults to the dataset item input
output?: unknown; // Output produced by the external runner
groundTruth?: unknown; // Defaults to the dataset item groundTruth
error?: { // Failure info when the item run failed
message: string;
stack?: string;
code?: string;
} | null;
startedAt?: string; // ISO date
completedAt?: string; // ISO date
traceId?: string;
scores?: Array<{ // Externally computed scores, persisted keyed by runId = experimentId
scorerId: string;
scorerName?: string;
score: number;
reason?: string;
metadata?: Record<string, unknown>;
}>;
}
Returns the persisted experiment result row.
Finalize experimentDirect link to Finalize experiment
POST /api/datasets/:datasetId/experiments/:experimentId/finalize takes no body. The server computes per-item counts from the persisted result rows and marks the experiment completed: succeededCount (at least one attempt without an error), failedCount (every attempt errored), and skippedCount (never submitted). Returns the updated experiment record.
Caller-driven experiment error responsesDirect link to Caller-driven experiment error responses
| Status | Condition |
|---|---|
400 | Result submitted to an experiment that has a target (EXPERIMENT_HAS_TARGET), item run requested on a target-less experiment (EXPERIMENT_HAS_NO_TARGET), targetType and targetId weren't provided together (EXPERIMENT_INVALID_TARGET), or the dataset has no items at the pinned version (EXPERIMENT_NO_ITEMS) |
404 | Dataset, experiment, dataset item, or target not found (EXPERIMENT_TARGET_NOT_FOUND) |
409 | Caller-supplied experiment id already exists for a different dataset or target (EXPERIMENT_ID_CONFLICT), or a call arrived after finalization (EXPERIMENT_ALREADY_FINALIZED) |
MCPDirect link to MCP
| Method | Path | Description |
|---|---|---|
GET | /api/mcp/servers | List MCP servers |
GET | /api/mcp/servers/:serverId/tools | List server tools |
POST | /api/mcp/:serverId | MCP HTTP transport |
GET | /api/mcp/:serverId/sse | MCP SSE transport |
Responses APIDirect link to Responses API
| Method | Path | Description |
|---|---|---|
POST | /api/v1/responses | Create a response through the OpenAI-compatible Responses API route |
GET | /api/v1/responses/:responseId | Retrieve a stored response |
DELETE | /api/v1/responses/:responseId | Delete a stored response |
For the full request and response contract, see the Responses API reference.
Conversations APIDirect link to Conversations API
| Method | Path | Description |
|---|---|---|
POST | /api/v1/conversations | Create a conversation |
GET | /api/v1/conversations/:conversationId | Retrieve a conversation |
DELETE | /api/v1/conversations/:conversationId | Delete a conversation |
GET | /api/v1/conversations/:conversationId/items | List stored items for a conversation |
For the full request and response contract, see the Conversations API reference.
LogsDirect link to Logs
| Method | Path | Description |
|---|---|---|
GET | /api/logs | List logs |
GET | /api/logs/:runId | Get logs by run ID |
Query parametersDirect link to Query parameters
{
page?: number;
perPage?: number;
transportId?: string;
}
TelemetryDirect link to Telemetry
| Method | Path | Description |
|---|---|---|
GET | /api/telemetry/traces | List traces |
GET | /api/telemetry/traces/:traceId | Get trace |
GET | /api/telemetry/traces/:traceId/spans | Get trace spans |
Common query parametersDirect link to Common query parameters
PaginationDirect link to Pagination
Most list endpoints support:
{
page?: number; // Page number (0-indexed)
perPage?: number; // Items per page (default: 10)
}
FilteringDirect link to Filtering
Workflow runs support:
{
fromDate?: string; // ISO date string
toDate?: string; // ISO date string
status?: string; // Run status filter
resourceId?: string; // Filter by resource
}
Error responsesDirect link to Error responses
All routes return errors in this format:
{
error: string; // Error message
details?: unknown; // Additional details
}
Common status codes:
| Code | Meaning |
|---|---|
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Missing/invalid auth |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Resource doesn't exist |
| 500 | Internal Server Error |
RelatedDirect link to Related
- createRoute(): Creating custom routes
- Server Adapters: Using adapters