AgentsSessions

Sessions

A session is one ongoing conversation between an agent and one contact on one channel: a customer in your Telegram group, a colleague in Slack, a caller on WhatsApp. The project’s runtime keeps every session’s transcript so the agent can carry context from message to message and so you can review what was said.

Sessions belong to a project. Any active member of the project can view them.

Today, sessions are viewed in the Console by signed-in members and are not available through API keys. In an agent loop you write yourself, the “session” is simply the messages array you send with each request, and storing it is up to you. A Sessions API is being built; its design is in Sessions API below.

What a session shows

FieldMeaning
IDThe session’s identifier. Use it to filter executions and to pick a session in Live activity.
ChannelThe service the conversation happens on, such as telegram, slack or whatsapp.
ContactWho the agent is talking to: their identity on that channel (a handle, number or user ID) and a display name when the channel provides one.
MessagesHow many messages have been exchanged so far.
StartedWhen the first message arrived.
Last messageWhen the most recent message was sent.
Statusactive, idle or closed.

Status

StatusMeaning
activeThe conversation is under way.
idleOpen, but nothing has been said for a while. A new message makes it active again.
closedFinished. A new message from the same contact starts or reopens a conversation.

The transcript

Opening a session shows every message in order. Each entry has a role and a time:

RoleWhat it is
userA message from the contact.
assistantThe agent’s reply.
toolA tool the agent called during the conversation, with the tool’s name and its result.
systemA note from the runtime, such as instructions added at the start.

Seeing tool entries in line with the conversation makes it easy to tell why the agent said something.

Browse sessions

Sessions are listed most recent first and load in pages as you scroll, 25 at a time by default and at most 100 per page. They can be narrowed to one channel.

Watch a session live

Each agent’s page has a Live activity panel that streams what the agent is doing in one session as it happens.

Open the agent

Go to AI Agents and select the agent.

Expand Live activity

Select Live activity to open the panel. Its badge shows the connection state: connecting, open, reconnecting or disconnected.

Pick a session

Choose from the most recent sessions (each listed with the contact, channel, status and last message time), or paste a session ID. The most recent session is selected for you if you have not picked one.

Read the stream

Two kinds of event appear as they happen:

  • Message events show the role, the first part of the text, the model’s response time, the prompt and completion token counts and the cost.
  • Tool events show the tool’s name, its status and how long it took.

The panel keeps the last 100 events. Reconnect reopens the stream and Clear empties the panel.

The stream reconnects by itself about once a minute and resumes from the last event it received, so a long session can be watched without gaps.

Sessions and executions

A session is the conversation; an execution is the record of one run that the conversation triggered. A long conversation can produce many runs.

To see every run a conversation caused:

  1. Copy the session ID.
  2. Open Executions, choose AI Agent runs and paste the ID into Session ID.
  3. Select Apply.

Privacy

Session transcripts can contain personal details sent by your contacts. Limit who has access to the project, and do not paste transcripts into public tickets.

Sessions API

⚠️
Preview — this feature is being built; the design below is the spec. Everything above this heading describes the Console as it works today.

The Sessions API lets your code do what a channel does today: open a conversation with an agent, send it messages, follow what it does as it happens, step in when it needs approval, and stop it. The agent runs on Syntic’s side, so your code does not have to host a loop, store history or execute built-in tools.

Use it when an agent should be part of your own product (a web app, a back-office job, a CI step) rather than reached through a messaging channel.

Endpoints

MethodPathPurpose
POST/v1/sessionsStart a session with an agent.
GET/v1/sessionsList sessions, newest first.
GET/v1/sessions/{session_id}Read a session’s status, usage and outcome.
POST/v1/sessions/{session_id}/eventsSend events: a user message, a tool result, an approval decision.
GET/v1/sessions/{session_id}/eventsRead the full event history, paginated.
GET/v1/sessions/{session_id}/streamFollow events live as server-sent events.
POST/v1/sessions/{session_id}/interruptStop the current turn. The session stays open.
POST/v1/sessions/{session_id}/archiveClose the session. No more events are accepted.
DELETE/v1/sessions/{session_id}Delete an archived session and its event history.

Base URL https://app.syntic.ai/v1 (the paths above already include /v1). Authenticate with a private key (Authorization: Bearer sk_live_...) and send syntic-beta: agents-2026-11-01 while the API is in preview. A key sees only sessions in its own workspace.

Start a session

POST /v1/sessions

FieldRequiredTypeNotes
agent_idYesstringAn active agent definition.
agent_versionNointegerPin a version. Default: the latest.
inputNoarrayEvents to send straight away, same format as POST /events. Saves a round trip.
titleNostringShown in the Console session list. Up to 200 characters.
contactNoobject{ "id": "...", "display_name": "..." }: who the agent is talking to, for your own tracking.
environment_idNostringOverride the agent’s environment.
memory_scopeNostringWhich scope of the agent’s memory stores this session reads and writes, such as customer:4821. Without it the agent can read only the global scope and cannot write.
limitsNoobjectLower the agent’s max_turns, session_token_budget or session_cost_budget_usd for this session. Higher values are refused.
metadataNoobjectUp to 16 string pairs.

Response 201 Created:

{
  "id": "sesn_01JC7M2K4P6R8T0V2X4Z",
  "type": "session",
  "agent_id": "agt_01JC4R8W6Q2N9X3T5V7Y",
  "agent_version": 4,
  "status": "running",
  "channel": "api",
  "title": "Refund for order 88123",
  "usage": { "input_tokens": 0, "cached_input_tokens": 0, "output_tokens": 0, "cost_usd": "0.0000" },
  "outcome": null,
  "created_at": "2026-11-05T10:00:00Z",
  "last_event_at": "2026-11-05T10:00:00Z",
  "metadata": {}
}
statusMeaning
runningThe agent is working on a turn.
requires_actionThe agent is waiting for you: a tool result from your code or an approval decision.
idleThe turn finished; the agent waits for the next message.
archivedClosed. Read-only.

outcome is filled in when a turn ends: { "reason": "end_turn" | "max_turns" | "budget_exhausted" | "interrupted" | "error", "message": "..." }.

Send events

POST /v1/sessions/{session_id}/events with { "events": [ ... ] }. Up to 20 events per call.

Event typeFieldsUse
user.messagecontent: a string, or an array of content parts as in chat completionsSay something to the agent. Starts a new turn if the session is idle; queued if running.
user.tool_resulttool_call_id, content, optional is_errorReturn the result of a function tool your code runs.
user.approvaltool_call_id, decision (approve or reject), optional reasonAnswer an approval request.
user.interruptnoneSame as POST /interrupt.

Response 202 Accepted with the stored events and their ids. Sending a user.tool_result or user.approval for a call that is not waiting gives 409 tool_call_not_pending.

Stream events

GET /v1/sessions/{session_id}/stream returns Content-Type: text/event-stream. Each event has an id: line (the event id) and a data: line with JSON. Reconnect with the Last-Event-ID header to resume without gaps; events are kept for replay for 24 hours. A comment line (: keep-alive) is sent every 15 seconds so proxies do not drop an idle connection. The stream ends when the session is archived; otherwise the server closes it after 10 minutes and you reconnect.

Event typePayloadMeaning
status.changedstatus, outcomeThe session’s status changed.
message.deltamessage_id, delta (text fragment)Part of the agent’s reply as it is written.
message.completedmessage_id, contentThe full reply. Always sent, even if you ignore deltas.
tool_call.startedtool_call_id, name, arguments, executor (syntic or client)The agent called a tool. If executor is client, the session moves to requires_action and waits for your user.tool_result.
tool_call.completedtool_call_id, output (truncated to 8 KB), is_error, duration_msA Syntic-run tool finished.
approval.requiredtool_call_id, name, argumentsA tool on the agent’s approval list wants to run.
usage.updatedusageRunning totals after each model call.
errorerror (same envelope as HTTP errors)Something failed inside the turn. Followed by status.changed.

Example stream:

id: evt_01JC7M3A
data: {"type":"status.changed","status":"running","outcome":null}

id: evt_01JC7M3B
data: {"type":"tool_call.started","tool_call_id":"call_7","name":"lookup_order","arguments":{"order_id":"88123"},"executor":"client"}

id: evt_01JC7M3C
data: {"type":"status.changed","status":"requires_action","outcome":null}
# Start a session with a first message
curl -sS https://app.syntic.ai/v1/sessions \
  -H "Authorization: Bearer $SYNTIC_API_KEY" \
  -H "Content-Type: application/json" \
  -H "syntic-beta: agents-2026-11-01" \
  -d '{"agent_id": "agt_01JC4R8W6Q2N9X3T5V7Y",
       "input": [{"type": "user.message", "content": "Please refund order 88123."}]}'
 
# Follow it
curl -sSN https://app.syntic.ai/v1/sessions/sesn_01JC7M2K4P6R8T0V2X4Z/stream \
  -H "Authorization: Bearer $SYNTIC_API_KEY" \
  -H "syntic-beta: agents-2026-11-01"

List and read history

GET /v1/sessions takes limit (1 to 100, default 20), after_id, before_id, agent_id, status and channel (api, telegram, slack, whatsapp and so on, so API and channel sessions appear in one list). GET /v1/sessions/{id}/events returns stored events oldest first with the same pagination; message.delta events are not stored, only message.completed.

Errors

Errors use the error envelope.

StatustypecodeWhen
400invalid_requestbeta_requiredsyntic-beta header missing.
400invalid_requestlimit_above_agentA session limit is higher than the agent’s.
402quota_exhaustedquota_exhaustedPlan allowance and credits cannot cover the next model call. The session moves to idle with outcome budget_exhausted.
404not_foundsession_not_found, agent_not_foundNo such id in this workspace.
409invalid_requestagent_archivedThe agent is archived.
409invalid_requestsession_archivedEvents sent to an archived session.
409invalid_requesttool_call_not_pendingTool result or approval for a call that is not waiting.
429rate_limitrate_limitedToo many requests.

Limits

LimitValue
Concurrent running sessions per workspace50 (raised with rate-limit tier)
Events per POST /events20
Size of one event1 MB
Wait for a client tool result or approval24 hours, then the turn ends with outcome error
Open stream connections per session5
Replay window for Last-Event-ID24 hours

Model tokens are billed exactly like chat completions, to the workspace of the key that started the session. Built-in tools add their own line items.

Build notes for the platform team

  • Back the API with the existing runtime and the execution store: each turn opens an execution with session_id set, so the Console’s Executions and Live activity views keep working unchanged.
  • The event type list above is the contract SDKs will wrap. Add new types only; never rename one.
  • Raw upstream error text must never appear in an error event; map it to the envelope first.
Was this page helpful?