Skip to main content
A Session is the unit of work in Qoder Cloud Agents. It pairs an Agent (configuration) with an Environment (infrastructure) to form a stateful conversation. You send messages to a Session and receive a stream of events back.

Session Lifecycle

1

Created → idle

A new Session starts in idle, awaiting input.
2

idle → processing

Sending a user.message event moves the Session into processing.
3

processing → idle

When the turn completes, the Session returns to idle. Repeat for each turn.
4

processing → canceling → idle

Cancelling a running Session transitions through canceling and then back to idle. The Session remains reusable.
5

archived (terminal)

Archival ends the Session permanently — it cannot be resumed.
A Session is a state machine. The core states are:
StateDescriptionTransitions to
idleIdle, awaiting a user messageprocessing
processingThe Agent is executingcanceling, idle
cancelingCancel requested, waiting for the turn to abortidle
archivedArchived— (terminal)

Field Reference

FieldTypeDescription
idstringSystem-generated, prefixed with sess_
typestringAlways "session"
agentstring/objectBound Agent. String = latest version; object {id, version} = pinned version. Creation requests only accept this field. The response always returns the full Agent object snapshot
agent_idstringReturned in responses only, always equals the bound Agent ID; kept for backward compatibility
environment_idstringThe bound Environment ID
statusstringCurrent state: idle / processing / canceling
turn_statusstringCurrent turn status: idle / running / canceling
titlestringOptional Session title; defaults to ""
memory_store_idsarrayMemory Store IDs referenced; defaults to []
vault_idsarrayVault IDs referenced; defaults to []
resourcesarrayResources attached to the Session (files etc.); defaults to []
created_atstringCreation timestamp
updated_atstringLast update timestamp
Token usage data (usage) is not returned by REST endpoints — it is only emitted in the SSE session.status_idle event at the end of each turn.

Create a Session

A Session needs both agent and environment_id.

Option 1: Pass an Agent ID (string)

Binds the latest version of the Agent:
# Create a Session with an Agent ID
curl -s -X POST https://api.qoder.com/api/v1/cloud/sessions \
  -H "Authorization: Bearer $QODER_PAT" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
    "environment_id": "env_019e44eb66bb748cabcd1489f6fa4428"
  }' | jq .

Option 2: Pass an Agent Object (pin a version)

Binds a specific version of the Agent for behavioral consistency:
# Pin a specific Agent version when creating the Session
curl -s -X POST https://api.qoder.com/api/v1/cloud/sessions \
  -H "Authorization: Bearer $QODER_PAT" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": {
      "id": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
      "version": 2
    },
    "environment_id": "env_019e44eb66bb748cabcd1489f6fa4428"
  }' | jq .
A successful call returns 201 Created:
{
  "id": "sess_019e5ce0bf9074b69c3481e93771a522",
  "agent": {
    "created_at": "2026-05-18T10:00:00Z",
    "default_environment": "",
    "description": "",
    "id": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
    "instructions": "You are a code review expert.",
    "mcp_servers": [],
    "model": "ultimate",
    "name": "code-reviewer",
    "system": "You are a code review expert.",
    "tools": [
      {
        "type": "agent_toolset_20260401",
        "enabled_tools": ["Bash", "Read", "Write"]
      }
    ],
    "type": "agent",
    "updated_at": "2026-05-18T10:00:00Z",
    "version": 2
  },
  "agent_id": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
  "environment_id": "env_019e44eb66bb748cabcd1489f6fa4428",
  "status": "idle",
  "title": "",
  "turn_status": "idle",
  "memory_store_ids": [],
  "resources": [],
  "vault_ids": [],
  "type": "session",
  "created_at": "2026-05-18T12:00:00Z",
  "updated_at": "2026-05-18T12:00:00Z"
}
In production, prefer Option 2 (pinned version) so Agent updates do not change Session behavior unexpectedly.

agent Field Format

FormatExampleBehavior
String"agent_019e5ce0bf307a1a8f952eb814aea3d5"Uses the latest version of the Agent
Object{"id": "agent_019e5ce0bf307a1a8f952eb814aea3d5", "version": 2}Pins to the specified version

State Transitions

Event request body format

POST /sessions/{id}/events requests must wrap events in an events array, and content is an array of content blocks:
FieldTypeRequiredDescription
eventsarrayYesEvent array — one or more events per request
events[].typestringYesEvent type, e.g. user.message
events[].contentarrayYesContent block array
events[].content[].typestringYesContent block type, e.g. text
events[].content[].textstringYesText content

idleprocessing

Sending a user.message event moves the Session from idle to processing:
# Send a message to trigger processing
curl -s -X POST "https://api.qoder.com/api/v1/cloud/sessions/sess_019e5ce0bf9074b69c3481e93771a522/events" \
  -H "Authorization: Bearer $QODER_PAT" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [{
      "type": "user.message",
      "content": [{"type": "text", "text": "Analyze the cyclomatic complexity of every Python file under the current directory."}]
    }]
  }' | jq .

processingidle

When the Agent finishes a turn the Session returns to idle. You will receive a session.status_idle event on the stream.

Cancel a Session

Interrupt a running Session:
# Cancel the Session
curl -s -X POST "https://api.qoder.com/api/v1/cloud/sessions/sess_019e5ce0bf9074b69c3481e93771a522/cancel" \
  -H "Authorization: Bearer $QODER_PAT"
Cancel only actually interrupts the Agent when the Session is processing. The Session moves to canceling, then back to idle once the turn aborts. The Session remains reusable — just send the next user.message. Cancelling an already-idle Session is a no-op: the call still returns 200 and the status stays idle.

Read Sessions

# Get a single Session
curl -s "https://api.qoder.com/api/v1/cloud/sessions/sess_019e5ce0bf9074b69c3481e93771a522" \
  -H "Authorization: Bearer $QODER_PAT"
# List Sessions with pagination
curl -s "https://api.qoder.com/api/v1/cloud/sessions?limit=10" \
  -H "Authorization: Bearer $QODER_PAT"
A paginated response:
{
  "data": [
    {
      "id": "sess_019e5ce0bf9074b69c3481e93771a522",
      "type": "session",
      "agent_id": "agent_019e5ce0bf307a1a8f952eb814aea3d5",
      "environment_id": "env_019e44eb66bb748cabcd1489f6fa4428",
      "status": "idle",
      "turn_status": "idle",
      "title": "",
      "memory_store_ids": [],
      "vault_ids": [],
      "resources": [],
      "created_at": "2026-05-18T12:00:00Z",
      "updated_at": "2026-05-18T12:30:00Z"
    }
  ],
  "first_id": "sess_019e5ce0bf9074b69c3481e93771a522",
  "last_id": "sess_019e5ce0bf9074b69c3481e93771a522",
  "has_more": false
}

Usage Stats

Per-turn token usage is not returned by REST endpoints. To inspect token usage, listen for the SSE session.status_idle event at the end of each turn — the usage field carries cumulative counters:
FieldDescription
usage.input_tokensInput tokens consumed by the turn
usage.output_tokensOutput tokens produced by the turn
usage.cache_read_input_tokensInput tokens served from cache
usage.cache_creation_input_tokensInput tokens written into cache
Example payload from the SSE stream:
{
  "type": "session.status_idle",
  "usage": {
    "input_tokens": 3840,
    "output_tokens": 2156,
    "cache_read_input_tokens": 0,
    "cache_creation_input_tokens": 0
  }
}

Session and Agent Version Binding

A Session snapshots its Agent configuration at creation time:
  • The string form "agent": "agent_xxx" binds the latest version at creation time.
  • The object form "agent": {"id": "agent_xxx", "version": N} pins to an exact version.
  • Once a Session exists, modifying the Agent does not change the Session.
  • To use a new Agent version, create a new Session.

Session A — bound to Agent v1

Created before the update. Continues to use v1 even after the Agent has been updated to v2.

Session B — bound to Agent v2

Created after the update. Uses the new v2 configuration.

Multi-Turn Conversations

Sessions support multi-turn conversations. After each message, wait for session.status_idle before sending the next:
#!/bin/bash
# Multi-turn conversation example
BASE_URL="https://api.qoder.com/api/v1/cloud"
SESSION_ID="sess_019e5ce0bf9074b69c3481e93771a522"
HEADERS=(
  -H "Authorization: Bearer $QODER_PAT"
)

# Turn 1: state the requirement
curl -s -X POST "$BASE_URL/sessions/$SESSION_ID/events" \
  "${HEADERS[@]}" \
  -H "Content-Type: application/json" \
  -d '{"events": [{"type": "user.message", "content": [{"type": "text", "text": "Scaffold a Python Flask project."}]}]}'

# Wait for processing to complete (poll or listen on SSE)
sleep 30

# Turn 2: add follow-up requirements
curl -s -X POST "$BASE_URL/sessions/$SESSION_ID/events" \
  "${HEADERS[@]}" \
  -H "Content-Type: application/json" \
  -d '{"events": [{"type": "user.message", "content": [{"type": "text", "text": "Add unit tests and a CI configuration to the project."}]}]}'
Common state-related errors when sending events to a Session:
HTTPTypeTrigger
409conflict_errorSending a message to a processing Session (cancel the current turn or wait for idle first)
404not_found_errorSession does not exist or has been deleted
409 error response example:
{
  "type": "error",
  "error": {
    "type": "conflict_error",
    "message": "Session is currently processing a turn. Cancel the current turn or wait for completion."
  }
}

Best Practices

  1. Pin versions — always create production Sessions with {"id": ..., "version": ...}.
  2. Cancel promptly — cancel Sessions you no longer need to free resources.
  3. Watch usage — review usage periodically to catch unexpected token spend.
  4. Use metadata — record business context (task ID, trigger source) in metadata.

FAQ

Q: Do Sessions time out? A: Sessions stay idle for a while; long-inactive Sessions may be archived automatically. Create Sessions on demand and cancel them when work completes. Q: What happens if I send a message to a processing Session? A: The request returns HTTP 409 conflict_error with the message Session is currently processing a turn. Cancel the current turn or wait for completion. Either cancel the current turn or wait until the Session returns to idle before sending the next message. Q: Is there a maximum number of turns per Session? A: There is no hard limit on turns, but the model context window applies. Older content may be truncated as the conversation grows. Q: How do I retrieve the full conversation history? A: Use GET /sessions/{id}/events to fetch every event for the Session, including user messages and Agent responses. Q: Can I keep using a Session after I cancel it? A: Yes. After cancel, the status moves from canceling back to idle and the Session is ready for the next user.message. Only archived is terminal — archived Sessions cannot be resumed. Q: Are files on the container disk preserved if a Session is idle for a long time? A: Not necessarily. Container temporary storage is retained for 24 hours only; after that the disk may be reclaimed. The Session itself remains usable and you can continue the conversation, but files previously produced on disk (cloned code, intermediate artifacts, etc.) will be lost. To persist files across days, upload critical artifacts via the Files API at the end of each turn. See Container Reference — File Persistence for details.

Next Steps