Platform

Sessions and turns

Keep a conversation going across requests, read its history, and handle stops and retries.

A session is a conversation that Blazing Agents stores for you. Pass its ID on the next call and the agent sees everything said so far, so your backend never has to save or replay message history. Each call that runs the agent is a turn. Every turn is metered, whether or not it belongs to a session.

Continue a conversation

This example starts a session, asks a follow-up in the same session, and prints the saved history. Set AGENT_ID to an agent with a provider and model, such as the one from the quickstart.

import { BlazingAgents, type UIMessage } from "@blazingagents/sdk";

const client = new BlazingAgents({
  apiKey: process.env.BLAZING_AGENTS_API_KEY!,
});
const agentId = process.env.AGENT_ID!;

function userMessage(text: string): UIMessage {
  return { id: crypto.randomUUID(), role: "user", parts: [{ type: "text", text }] };
}

const first = await client.chat({
  agentId,
  message: userMessage("Plan a weekend in Lisbon."),
});
const sessionId = await first.sessionId;
await first.toResponse().text();

const followUp = userMessage("Make it suitable for children.");
const second = await client.chat({ agentId, sessionId, message: followUp });
await second.toResponse().text();

const history = await client.sessions.messages({ agentId, sessionId });
for (const message of history.data) console.log(message.role, message.id);

You see four lines: user, assistant, user, assistant. Reading the stream to the end lets each turn finish before the next one starts. In a real app you return the stream to your frontend instead of reading it yourself.

How sessions behave

Calling client.chat() without a session ID starts a new session. You get the ss_... ID before the answer finishes streaming, so save it right away. Pass it back as sessionId (session_id in Python) to continue.

A session belongs to one agent. Resuming it through another agent, or resuming a deleted or unknown session, returns not_found. Blazing Agents never quietly creates a replacement.

A successful turn saves the user message, the assistant reply, and its tool activity together. A failed or cancelled turn is still metered, but it adds nothing to the history. If the very first turn fails, you keep an empty session that you can still use.

Stop and resend

Treat a turn as done only when its stream finishes normally. A 200 status or a closed connection alone does not prove success. Keep the user's draft until then, so they can edit and send it again after an error or a stop.

  • Stopping ends your stream and asks Blazing Agents to cancel the turn. The exchange may already be saved, so reload the history rather than guessing.
  • A resend is an ordinary new message with a fresh message ID. You do not need to poll for the outcome of the earlier attempt.
  • Keep using the session ID you received, even if that session is still empty.
  • Sending again can repeat a tool's side effects, such as a sent email.

The chatbot guide shows send, stop, edit, and regenerate end to end.

Read the history

client.sessions.messages() returns AI SDK UIMessage objects, oldest first within each page. With no cursor you get the newest page. Pass nextCursor back as cursor to load older pages, or pass latestCursor back as after to fetch only messages added since your last read. See sessions.messages() for page sizes and cursor rules.

To show an agent's conversations, call client.sessions.list({ agentId }). For an inbox across all agents, call client.sessions.listLatest({ byAgent: true }) to get each agent's latest session in one request.

Files the agent deliberately published during a conversation are artifacts. List them with client.artifacts.list({ sessionId }).

Pin a version and label the user

By default, each turn runs the agent's latest configuration. Pass version when you start a session to pin it to one agent version for its whole life. You cannot change the pin on later turns.

Pass userId and metadata on the first turn to label the session with your end user. The userId is fixed once the session starts, and it labels usage for reporting. It does not control access, so your backend still decides who may open which session. See tenancy and attribution.

Busy and concurrent sessions

A session returns session_busy (HTTP 409) while a tool approval is waiting for a decision or an approved call is still running. Show the error and let the user send again later. Deleting a session also returns session_busy while an approved call is running.

If two turns run on the same session at once, the first to finish is saved and the other fails instead of merging the histories. Send one turn at a time per session when order matters.

Chat endpoint pattern

In a real app, your backend maps each of your own chats to one session. The handler authorizes the request, loads the saved session ID, and relays the stream. Here app holds your own sign-in and storage code:

import { client } from "./client.ts";
import * as app from "./app.ts";

const agentId = app.requireEnv("BLAZING_AGENTS_AGENT_ID");

export async function handleChat(request: Request): Promise<Response> {
  const { chatId, userId, message } = await app.authorize(request);
  const existingSessionId = await app.loadSessionId(userId, chatId);
  const result = await client.chat({
    agentId,
    ...(existingSessionId ? { sessionId: existingSessionId } : {}),
    message,
    abortSignal: request.signal,
    userId,
  });
  if (!existingSessionId) {
    await app.saveSessionId(userId, chatId, await result.sessionId);
  }
  return result.toResponse();
}

Keep these rules in your handler:

  • Take the agent ID, session ID, and userId from your own server-side state, never from the request body.
  • Accept exactly one new user message per request. The session already holds the rest.
  • Save the new session ID before you relay the stream. If two first requests for the same chat can arrive together, hold a lock until the ID is saved so you do not create two sessions.
  • Return result.toResponse() unchanged so the stream and its headers reach the browser intact, and forward the request's abort signal so a closed tab cancels the turn.

Connect Blazing Agents to your app walks through this endpoint step by step.

Send images and regenerate answers

To send an image, add a file part with an image/... media type and a URL, such as a data URL, to the user message.

To replace the latest answer, resume the session with trigger: "regenerate-message" and send the user message again. Continuing the first example:

const retry = await client.chat({
  agentId,
  sessionId,
  message: followUp,
  trigger: "regenerate-message",
});
await retry.toResponse().text();

The new answer replaces the old one only if the turn succeeds. If it fails or you stop it, the previous answer stays. To replace an earlier answer instead, pass its ID as messageId (message_id in Python).

Next

On this page