Skip to content

Chat & Session Management API

Endpoints for sending chat messages, retrieving conversation history, and managing chat sessions.

Base URL

BASE_URL = "https://api.fermi.dev"

All chat routes below are relative to this base URL. Full example: POST ${BASE_URL}/agents-service/api/v1/agentcore/chat/stream

Authentication

Authorization: Bearer <token>

Tenant and user come from the JWT. Do not send tenant_id or user_id in the request body.

Chat flow

  1. Mint a session id (fermi-{uuid}) or reuse one from the session list.
  2. Optional: upload files with POST /agents-service/api/v1/agentcore/chat/files.
  3. POST /agents-service/api/v1/agentcore/chat/stream with { "message", "session_id" }.
  4. If the model pauses for human input, POST /agents-service/api/v1/agentcore/chat/resume with tool_call_id and the tool result.
  5. List, reload, or delete sessions on /agents-service/api/v1/agentcore/sessions.

Always use session_id from the JSON response (non-stream) or from the session list. The API may return a different id than the one you sent.

threadId is accepted as an alias of session_id on chat bodies.

Do not send Ask Fermi traffic to POST /public/v1/analytics/chat. That path is for onboarding and graph_modification only.

Stream a message

POST /agents-service/api/v1/agentcore/chat/stream

Response is assistant-ui / Vercel AI SDK data-stream v1 (text/plain), not JSON.

Headers:

Authorization: Bearer <token>
Content-Type: application/json

Response headers include x-vercel-ai-data-stream: v1.

Request Body:

{
  "message": "What were last month sales by region?",
  "session_id": "my-chat-session-123"
}
Field Type Required Description
message string Yes unless messages or attachments User question
messages array Alternative to message assistant-ui turns; last role: "user" text is used
session_id string No Session to continue; also accepted as threadId
attachments array No { "s3_key": "...", "name"?, "content_type"?, "size"? } from the files upload

cURL:

curl --request POST \
  --url "${BASE_URL}/agents-service/api/v1/agentcore/chat/stream" \
  --header "Authorization: Bearer <token>" \
  --header "Content-Type: application/json" \
  --data '{
    "message": "What were last month sales by region?",
    "session_id": "my-chat-session-123"
  }'

Stream body: one chunk per line, {type}:{json}\n. Text deltas are type 0. The stream ends with a type d finish payload (finishReason is stop, tool-calls, or error). Tool calls use types b / c / a.

Errors: 400 invalid JSON or missing user text (unless attachments are present), 422 body validation, 503 chat runtime not configured or invoke failed.

Non-streaming chat

POST /agents-service/api/v1/agentcore/chat

Same request body as stream. Waits for the full run. The response includes session_id — store that value for messages, cancel, and delete.

{
  "session_id": "my-chat-session-123",
  "answer": "Last month sales by region were..."
}

Async chat (no long HTTP hold)

POST /agents-service/api/v1/agentcore/chat/async

callback_url is required (HTTPS). Omitting it returns 422. Request body:

{
  "session_id": "my-chat-session-123",
  "callback_url": "https://example.com/hooks/fermi-chat",
  "message": "What were last month sales by region?"
}

Returns 202:

{
  "session_id": "my-chat-session-123",
  "status": "accepted"
}

When the run finishes, Fermi POSTs JSON to callback_url:

{
  "session_id": "my-chat-session-123",
  "status": "completed",
  "answer": "..."
}

On failure: { "session_id", "status": "failed", "error" }.

Resume after human input

POST /agents-service/api/v1/agentcore/chat/resume

When the stream finishes with finishReason: "tool-calls" (typically request_human_input), POST the human result. tool_call_id is required (422 without it). Response is the same data-stream as /chat/stream.

{
  "session_id": "my-chat-session-123",
  "tool_call_id": "tooluse-abc",
  "tool_name": "request_human_input",
  "tool_input": {},
  "result": { "answers": ["Yes"] }
}
Field Type Required
tool_call_id string Yes
tool_input object Yes
result any Yes
tool_name string No (default request_human_input)
session_id / threadId string No

Cancel a run

POST /agents-service/api/v1/agentcore/chat/cancel

{
  "session_id": "my-chat-session-123"
}
{
  "session_id": "my-chat-session-123",
  "cancelled": true,
  "had_live_run": true,
  "runtime_stopped": true
}

Reattach to an in-flight stream

POST /agents-service/api/v1/agentcore/chat/attach

Body: { "session_id" } (or threadId). Returns data-stream v1 by polling session memory. Use this after refresh while a turn is still running.

List sessions

GET /agents-service/api/v1/agentcore/sessions

curl --request GET \
  --url "${BASE_URL}/agents-service/api/v1/agentcore/sessions?max_results=20" \
  --header "Authorization: Bearer <token>"
Query Type Default Description
max_results integer 20 1–100
next_token string none Activity cursor from the previous page
include_preview boolean true Include sidebar preview text
{
  "sessions": [
    {
      "sessionId": "my-chat-session-123",
      "actorId": "user-123",
      "createdAt": "2026-08-26T10:00:00+00:00",
      "lastMessageAt": "2026-08-26T10:02:10+00:00",
      "preview": "What were last month sales by region?",
      "running": false,
      "requiresAction": false
    }
  ],
  "nextToken": null
}

Sessions are ranked by last activity. nextToken is an activity cursor.

Session messages

GET /agents-service/api/v1/agentcore/sessions/{session_id}/messages

curl --request GET \
  --url "${BASE_URL}/agents-service/api/v1/agentcore/sessions/my-chat-session-123/messages?max_results=100" \
  --header "Authorization: Bearer <token>"
Query Type Default Description
max_results integer 100 1–100
include_partial boolean false Keep an in-flight assistant turn (reconnect)

Use max_results. Other page-size query names are ignored.

{
  "sessionId": "my-chat-session-123",
  "messages": [
    {
      "id": "evt-1",
      "role": "user",
      "content": [{ "type": "text", "text": "Hi, how are you?" }]
    },
    {
      "id": "evt-2",
      "role": "assistant",
      "content": [{ "type": "text", "text": "Your email has been sent." }],
      "status": "complete"
    }
  ],
  "preview": "Hi, how are you?",
  "running": false,
  "requiresAction": false
}

Assistant content may include { "type": "tool-call", "toolCallId", "toolName", "args", "result?" }.

Session run status

GET /agents-service/api/v1/agentcore/sessions/{session_id}/status

{
  "sessionId": "my-chat-session-123",
  "running": false
}

Delete a session

DELETE /agents-service/api/v1/agentcore/sessions/{session_id}

{
  "sessionId": "my-chat-session-123",
  "deletedEvents": 12
}

If the session has no events: { "sessionId", "deletedEvents": 0 }.

Chat files

GET /agents-service/api/v1/agentcore/chat/files/limits

{
  "max_bytes": 524288000,
  "max_files": 10
}

Treat the live /limits response as source of truth.

POST /agents-service/api/v1/agentcore/chat/filesmultipart/form-data

Field Type Required
session_id string Yes
file file Yes
{
  "id": "example-org/example-user/my-chat-session-123/file.pdf",
  "name": "file.pdf",
  "s3_key": "example-org/example-user/my-chat-session-123/file.pdf",
  "bucket": "example-bucket",
  "content_type": "application/pdf",
  "size": 12345,
  "s3_uri": "s3://example-bucket/example-org/example-user/my-chat-session-123/file.pdf",
  "session_id": "my-chat-session-123"
}

Pass s3_key (and optional name / content_type / size) in the next /chat/stream attachments array. Do not send file bytes on the chat POST.

GET /agents-service/api/v1/agentcore/chat/files?session_id={ "session_id", "files" }

DELETE /agents-service/api/v1/agentcore/chat/files?s3_key=&session_id={ "deleted": true, "s3_key" }

GET /agents-service/api/v1/agentcore/chat/files/url?s3_key=&session_id={ "url", "s3_key" } (presigned GET)

Summary

Method Path Notes
POST /agents-service/api/v1/agentcore/chat/stream Send; data-stream v1
POST /agents-service/api/v1/agentcore/chat JSON { session_id, answer }
POST /agents-service/api/v1/agentcore/chat/async 202 + callback_url
POST /agents-service/api/v1/agentcore/chat/resume Continue after human input; data-stream
POST /agents-service/api/v1/agentcore/chat/cancel Stop in-flight run
POST /agents-service/api/v1/agentcore/chat/attach Reconnect in-flight; data-stream
GET /agents-service/api/v1/agentcore/sessions { sessions, nextToken }
GET /agents-service/api/v1/agentcore/sessions/{id}/messages History
GET /agents-service/api/v1/agentcore/sessions/{id}/status { running }
DELETE /agents-service/api/v1/agentcore/sessions/{id} { deletedEvents }
GET/POST/DELETE /agents-service/api/v1/agentcore/chat/files Attachments

Next Steps