Skip to main content
A Session is a running workspace for an Agent. It binds an Agent snapshot to an Environment, optional resources, and optional Vault credentials. New Sessions start in idle; send events to start work.

Session Status Lifecycle

A Session is a state machine. The status field on a Session resource takes one of the following values:
StatusDescriptionTransitions to
idleSession is idle and ready to receive a messagerunning, rescheduling, terminated
runningAgent is processing a turnidle, rescheduling, terminated
reschedulingThe underlying runtime is being rescheduled; the Session is unavailable until it returns to idleidle, terminated
terminatedSession has been terminated (final state)
Two additional lifecycle markers are surfaced outside the status field:
  • Archived: signalled by a non-null archived_at timestamp. The status value itself does not change to archived — the Session remains readable but rejects new events.
  • Cancel response: the POST /api/v1/cloud/sessions/{id}/cancel endpoint always returns a fixed body literal "status": "canceling". This is a response shape, not a Session status — the persisted status reverts to idle once the turn aborts.
1

Created → idle

A new Session starts in idle, awaiting input.
2

idle → running

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

running → idle

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

running → idle (after cancel)

Cancelling a running Session aborts the turn and the Session returns to idle. The cancel response body uses a fixed "status": "canceling" literal regardless of the persisted status. The Session remains reusable.
5

rescheduling

The runtime may temporarily move into rescheduling while the Session is being rescheduled; it returns to idle once recovery completes.
6

archived / terminated (terminal)

Archival (via archived_at) or termination ends the Session permanently — it cannot be resumed.

Cancel Semantics

  • Cancel on idle: No-op. Returns HTTP 200 and the status stays idle.
  • Cancel on running: Interrupts the Agent. Returns HTTP 202. The Session transitions to canceling, then back to idle once the turn aborts.
  • After cancel: The Session remains reusable — send the next user.message to start a new turn.
# Cancel the current turn
curl -s -X POST "https://api.qoder.com/api/v1/cloud/sessions/$SESSION_ID/cancel" \
  -H "Authorization: Bearer $QODER_PAT"
Only archived and terminated are terminal states. A cancelled Session always returns to idle and can continue accepting messages.

Sending Messages to a Running Session (409 Error)

If you send a user.message to a Session that is currently running, the API returns HTTP 409:
{
  "type": "error",
  "request_id": "cb80235f-76a2-4ff3-9e28-5aa2da12dc14",
  "error": {
    "type": "invalid_request_error",
    "message": "Session is currently processing a turn. Cancel the current turn or wait for completion."
  }
}
This is the most common pitfall for new users. Always wait for session.status_idle before sending the next message, or cancel the current turn first.

Create a Session

Create a Session with an existing agent and environment_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": {"id": "agent_019e390add9f7bac9b6cc806db46fcbd", "type": "agent", "version": 2},
    "environment_id": "env_019e2590d33f711fabf42f2857cecd8a",
    "title": "Code review session",
    "metadata": {"purpose": "review"}
  }'
The create response is a Session object. It includes agent, environment_id, status, resources, vault_ids, deployment_id, outcome_evaluations, stats, environment_variables, archived_at, created_at, and updated_at. Legacy request fields such as environment, delta_flush_interval_ms, memory_store_ids, and vaults are not supported. environment_variables is supported again — see Create a session for the JSON-string request format and validation rules.

Attach Resources at Creation

Attach files, repositories, and Memory Stores in the resources array:
{
  "resources": [
    {
      "type": "file",
      "file_id": "file_019e5ce0bf307a1a8f952eb814aea3d5",
      "mount_path": "/data/input/spec.md"
    },
    {
      "type": "github_repository",
      "url": "https://github.com/your-org/your-repo",
      "mount_path": "/data/workspace/your-repo",
      "authorization_token": "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    },
    {
      "type": "memory_store",
      "memory_store_id": "memstore_019eed05b61e78cea61bfd366e072878",
      "access": "read_write",
      "instructions": "Use this memory for long-lived project context."
    }
  ]
}
To add a file after creation, use POST /api/v1/cloud/sessions/{session_id}/resources. Current CAS supports post-create add only for file resources. Use the resource list/get/update/delete endpoints to inspect, rotate a GitHub token, or remove resources.

Send Messages

Send user.message events through the Events API. content must be a non-empty array of content blocks:
curl -s -X POST "https://api.qoder.com/api/v1/cloud/sessions/$SESSION_ID/events" \
  -H "Authorization: Bearer $QODER_PAT" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "type": "user.message",
        "content": [
          {"type": "text", "text": "Review this repository and summarize the risks."}
        ]
      }
    ]
  }'
The send-events endpoint returns HTTP 200 with {"data":[...]}. It accepts these client event types: user.message, user.interrupt, user.tool_confirmation, user.tool_result, user.custom_tool_result, user.define_outcome, and system.message.

Read Events

Use the event stream for live updates:
curl -s -N "https://api.qoder.com/api/v1/cloud/sessions/$SESSION_ID/events/stream" \
  -H "Authorization: Bearer $QODER_PAT" \
  -H "Accept: text/event-stream"
The stream emits Server-Sent Events with id, event, and data. The stream endpoint supports the Last-Event-ID header for reconnection replay; event type query filters are not currently supported. To receive incremental streaming events, set incremental_streaming_enabled: true when creating the Session. The stream can then include agent.message_start, agent.content_block_delta, and related incremental events before the final full agent.message. See Incremental streaming for a quick verification flow. Use the list endpoint for history and pagination:
curl -s "https://api.qoder.com/api/v1/cloud/sessions/$SESSION_ID/events?limit=20&order=desc" \
  -H "Authorization: Bearer $QODER_PAT"
List responses use data and next_page.

Read and Update Sessions

# Get one Session
curl -s "https://api.qoder.com/api/v1/cloud/sessions/$SESSION_ID" \
  -H "Authorization: Bearer $QODER_PAT"

# List Sessions
curl -s "https://api.qoder.com/api/v1/cloud/sessions?limit=20" \
  -H "Authorization: Bearer $QODER_PAT"

# Update title, metadata, or Agent configuration (tools, MCP servers)
curl -s -X POST "https://api.qoder.com/api/v1/cloud/sessions/$SESSION_ID" \
  -H "Authorization: Bearer $QODER_PAT" \
  -H "Content-Type: application/json" \
  -d '{"title":"Updated title","metadata":{"priority":"high"}}'
Session list uses page / next_page pagination and supports filters such as agent_id, agent_version, deployment_id, memory_store_id, statuses, and created_at[...].

Threads

Managed-agent Sessions can have a coordinator thread and child threads. Thread endpoints use the public session_thread shape and do not include legacy thread fields such as role, name, agent_id, agent_version, or stop_reason.
curl -s "https://api.qoder.com/api/v1/cloud/sessions/$SESSION_ID/threads" \
  -H "Authorization: Bearer $QODER_PAT"
Child threads can be archived with POST /api/v1/cloud/sessions/{session_id}/threads/{thread_id}/archive. Current CAS returns 409 when asked to archive the coordinator/main thread.

Lifecycle

Archive a Session when it should no longer be used:
curl -s -X POST "https://api.qoder.com/api/v1/cloud/sessions/$SESSION_ID/archive" \
  -H "Authorization: Bearer $QODER_PAT"
Delete a Session when you need removal confirmation:
curl -s -X DELETE "https://api.qoder.com/api/v1/cloud/sessions/$SESSION_ID" \
  -H "Authorization: Bearer $QODER_PAT"
Delete returns:
{
  "id": "sess_019e3bb1e8c171fd9abbb1477ffb84cc",
  "type": "session_deleted"
}
The cancel endpoint returns {"id":"...","type":"session","status":"canceling"}. It responds with 202 Accepted when there is an active turn to cancel, and 200 OK (no-op) when the Session is already idle.

Multi-Turn Conversation Workflow

Sessions support multi-turn conversations. The recommended pattern is:
  1. Send a user.message event.
  2. Listen to the SSE stream for updates.
  3. Wait for the session.status_idle event.
  4. Send the next user.message.
#!/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."}]}]}'
Always wait for session.status_idle before sending the next message. Sending a message while the Session is still running returns HTTP 409.

Best practices

  1. Pin Agent versions — In production, always create Sessions with {"id": ..., "type": "agent", "version": ...} so Agent updates do not change Session behavior unexpectedly.
  2. Use metadata — Record business context (task ID, trigger source, etc.) in the metadata field for traceability and debugging.
  3. Cancel promptly — Cancel Sessions you no longer need to free compute resources.

FAQ

Q: What happens if I send a message to a running Session? A: The API returns HTTP 409 with type: "invalid_request_error" and 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: Can I still use a Session after cancelling? A: Yes. After cancel, the Session transitions from canceling back to idle. You can continue the conversation by sending the next user.message. Only archived and terminated are terminal states. Q: How do I get the full conversation history? A: Use GET /api/v1/cloud/sessions/{id}/events to retrieve all events for the Session, including user messages and Agent responses. Q: How do I reconnect after an SSE disconnect? A: Pass the Last-Event-ID header when reconnecting to the SSE stream endpoint. The server will replay events from after that ID. Q: GET /api/v1/cloud/environments returns an empty array? A: Check that your Personal Access Token (PAT) has the required permissions for the target workspace. Environment access is scoped to the authenticated user’s permissions.

API Reference