Run Endpoint
Overview
POST /api/agents/{agentId}/run is the single HTTP entry point for invoking an agent. It supports three execution modes: persisted chat (server-managed history), stateless chat (client-managed history), and prompt-only execution (template variables with no user turn).
| Property | Value |
|---|---|
| Method | POST |
| Endpoint | /api/agents/{agentId}/run |
| Authentication | Authorization (user JWT) or cf-api-key |
Use the Accept header to choose how the response is delivered. Set request body fields (message, messages, or vars) to select an execution mode. See Execution Modes for details.
API Contract
Headers
| Header | Required | Description |
|---|---|---|
Content-Type | Yes | application/json, or multipart/form-data for file uploads |
Accept | No | application/json (default) or text/event-stream |
Authorization or cf-api-key | Yes | See Authentication |
Accept header | Response |
|---|---|
text/event-stream | SSE stream (AI SDK UI Message Stream ) |
application/json (default) | Single JSON object { text, usage, finishReason, conversationId } |
Mode 1 responses may include X-Conversation-Id when a new conversation is created.
Authentication
Bearer Token
Authorization: Bearer <supabase_jwt>Use for platform users with an active Supabase session.
Optional Headers
Pass additional metadata to downstream MCP integrations:
x-agent-payload: {"x-user-id":"123","x-tenant-id":"acme"}- Must contain valid JSON.
- Only string, number, and boolean values are preserved.
- Unsafe headers such as
authorization,host, andcontent-lengthare removed before forwarding.
Request Body
| Field | Type | Modes | Description |
|---|---|---|---|
message | UIMessage | 1 | Latest user turn (server-managed history) |
messages | UIMessage[] | 2 | Full client-owned transcript |
conversationId | string | 1 | Conversation UUID; omit on first turn |
vars | Record<string, any> | 1, 2, 3 | Template variables for the agent prompt |
Response
JSON (Accept: application/json)
| Field | Type | Description |
|---|---|---|
text | string | Generated output |
usage | object | Token usage |
finishReason | string | Why generation stopped |
conversationId | string | null | Set when Mode 1 persistence is active |
{
"text": "Generated output from the agent",
"usage": {
"inputTokens": 123,
"outputTokens": 456,
"totalTokens": 579
},
"finishReason": "stop",
"conversationId": "550e8400-e29b-41d4-a716-446655440000"
}Streaming (Accept: text/event-stream)
Returns an AI SDK UI Message Stream over SSE. Do not call response.json(); consume the stream incrementally.
Error Responses
| Status | Response |
|---|---|
400 | { "error": "Agent is not active" } |
400 | { "error": "Provide either \"message\" or \"messages\", not both" } |
400 | { "error": "No valid messages to process" } |
401 | { "error": "Unauthorized" } |
403 | { "error": "Forbidden" } |
404 | { "error": "Agent not found" } |
500 | { "error": "Internal server error" } |
UIMessage Format
Structured messages follow the AI SDK UIMessage type, the same model used by useChat and the platform dashboard.
{
"id": "msg-1",
"role": "user",
"parts": [
{
"type": "text",
"text": "Hello"
}
]
}| Part type | Purpose | Reference |
|---|---|---|
text | User or assistant text | TextUIPart |
file | Image, PDF, or document attachment | FileUIPart |
Assistant messages may also include tool-*, reasoning, and other part types when consuming streaming responses.
Execution Modes
Every request uses one of three execution modes. The mode is determined by which body fields you send, not by a separate mode flag.
Mode 1: Persisted (server-managed history)
Send the latest user turn only. Chocolate Factory loads prior turns from the database when conversationId is set, persists new messages, and returns X-Conversation-Id on the first turn.
| Field | Value |
|---|---|
message | Latest UIMessage |
conversationId | Optional UUID. Omit on the first turn |
Accept | text/event-stream (chat UI) or application/json |
First turn
POST /api/agents/{agentId}/run
Accept: text/event-stream
Content-Type: application/json
cf-api-key: YOUR_API_KEY{
"message": {
"role": "user",
"parts": [{ "type": "text", "text": "Hello" }]
}
}Read X-Conversation-Id from the response headers and send it on subsequent turns:
{
"conversationId": "550e8400-e29b-41d4-a716-446655440000",
"message": {
"role": "user",
"parts": [{ "type": "text", "text": "What did I just say?" }]
}
}Retrieve stored history later with GET /api/agents/{agentId}/conversations/{conversationId}.
Mode 2: Stateless (client-managed history)
Send the full transcript on every request. Nothing is stored server-side. Use this when your application owns conversation state.
| Field | Value |
|---|---|
messages | Complete UIMessage[] array |
Accept | Usually text/event-stream |
{
"messages": [
{
"role": "user",
"parts": [{ "type": "text", "text": "Hello" }]
},
{
"role": "assistant",
"parts": [{ "type": "text", "text": "Hi! How can I help?" }]
},
{
"role": "user",
"parts": [{ "type": "text", "text": "Summarize our chat so far." }]
}
]
}Provide either message (Mode 1) or messages (Mode 2), never both.
Mode 3: Prompt-only (template execution)
Run the agent from its system prompt and template variables with no user turn. Typically used with Accept: application/json for forms, content generation, and backend jobs.
| Field | Value |
|---|---|
vars | Template variables referenced by the agent prompt |
Accept | application/json |
{
"vars": {
"title": "AI in logistics",
"tone": "professional",
"keywords": "automation, supply chain, forecasting"
}
}You can combine vars with Mode 1 or Mode 2 when the agent prompt references runtime variables alongside user input.
File Attachments
Attach files by including FileUIPart entries in the parts array of your message or the last item in messages.
FileUIPart shape
{
"type": "file",
"url": "https://example.com/report.pdf",
"filename": "report.pdf",
"mediaType": "application/pdf"
}url: HTTPS URL to a hosted file, or adata:URL (base64-encoded). Hosted URLs are passed through; data URLs are uploaded server-side automatically.filename: Original file name (recommended).mediaType: IANA media type (e.g.image/png,application/pdf).
Supported types
| Category | Types |
|---|---|
| Images | JPEG, PNG, GIF, WebP |
| Documents | PDF, TXT, MD, DOCX, XLSX, XLS |
Maximum size: 25 MB per file.
Hosted file URL
{
"message": {
"role": "user",
"parts": [
{ "type": "text", "text": "Summarize this PDF" },
{
"type": "file",
"url": "https://example.com/report.pdf",
"filename": "report.pdf",
"mediaType": "application/pdf"
}
]
}
}Integration Examples
Mode 1: Persisted
Server-managed history. Send only the latest turn. Read X-Conversation-Id from the response headers on the first turn and reuse it on subsequent requests.
fetch
const response = await fetch(`/api/agents/${agentId}/run`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"cf-api-key": "YOUR_API_KEY",
},
body: JSON.stringify({
conversationId: "550e8400-e29b-41d4-a716-446655440000",
message: {
role: "user",
parts: [{ type: "text", text: "Write a product announcement email" }],
},
vars: {
productName: "Chocolate Factory AI",
audience: "existing customers",
},
}),
});
const data = await response.json();
console.log(data.text);Best Practices
- Mode 1 (persisted): chat widgets and platform-style UIs where Chocolate Factory should own history.
- Mode 2 (stateless): integrations that already store transcripts externally.
- Mode 3 (prompt-only): forms, batch jobs, and template-driven generation with
Accept: application/json. - Reuse
conversationIdacross turns in Mode 1. - Attach files as
FileUIPart(type: "file"). See UIMessage docs . - Use
x-agent-payloadwhen MCP tools need extra HTTP headers. - Inspect
usageto monitor token consumption.