Skip to main content
Delegate work to your agent

Sessions

Create, run, inspect, and archive Cloud Agent Sessions.

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.

Dynamic runtime configuration updates

Existing Sessions use pinned runtime snapshots. Updating an Agent or publishing a new Agent version does not change them, and a Skill binding pinned to a numeric version stays on that version when a newer Skill version is published. To apply model, system prompt, tool, MCP server, Skill-binding, name, or description changes, call Update a session with the corresponding agent fields. Skill bindings with an omitted version or "latest" follow the newest Skill version at sandbox preparation time.

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, terminated
runningAgent is processing a turnidle, rescheduling, terminated
reschedulingA model request hit a recoverable error and the service is retrying automatically; the current turn is still activerunning, idle, 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

running → rescheduling → running / idle

A Session may temporarily enter rescheduling during an automatic model retry. It returns to running when the next attempt starts, or to idle when the current turn ends.
6

archived / terminated (terminal)

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

Automatic model request retries

When the service classifies a model request error as recoverable, it retries automatically and the Session or Thread may temporarily enter rescheduling. This is still part of the current turn. The client does not need to resend user.message; keep the event stream open and wait for the status to return to running or idle. If an error occurs after the model has produced a response chunk, the service does not replay the request, avoiding duplicate output or tool calls. If session.error is received, use error.retry_status.type to determine whether the service will retry. Use subsequent Session/Thread status events as the source of truth for the final outcome.

Cancel Semantics

  • Cancel on idle: No-op. Returns HTTP 200 and the status stays idle.
  • Cancel with an active turn: Applies in both running and rescheduling. Returns HTTP 202; the response status is canceling, and the Session returns 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.cn/api/v1/cloud/sessions/$SESSION_ID/cancel" \
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN"
Only archived and terminated are terminal states. A cancelled Session always returns to idle and can continue accepting messages.

Sending Messages While a Turn Is Active (409 Error)

If you send a new user.message while a Session is processing a turn, including while it is running or rescheduling, 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.cn/api/v1/cloud/sessions" \
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN" \
  -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, usage, environment_variables, archived_at, created_at, and updated_at. usage.total_credits is a cumulative snapshot of credits consumed by recorded model calls and is floored to at most 2 decimal places. See Create a session for the environment_variables string-to-string map 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 the Add a Session resource API. 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.cn/api/v1/cloud/sessions/$SESSION_ID/events" \
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN" \
  -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, and user.define_outcome.

Read Events

Use the event stream for live updates:
curl -s -N "https://api.qoder.com.cn/api/v1/cloud/sessions/$SESSION_ID/events/stream" \
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN" \
  -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. Use the list endpoint for history and pagination:
curl -s "https://api.qoder.com.cn/api/v1/cloud/sessions/$SESSION_ID/events?limit=20&order=desc" \
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN"
List responses use data and next_page.

Read and Update Sessions

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

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

# Update title, metadata, or Agent configuration (tools, MCP servers)
curl -s -X POST "https://api.qoder.com.cn/api/v1/cloud/sessions/$SESSION_ID" \
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN" \
  -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.cn/api/v1/cloud/sessions/$SESSION_ID/threads" \
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN"
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.cn/api/v1/cloud/sessions/$SESSION_ID/archive" \
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN"
Delete a Session when you need removal confirmation:
curl -s -X DELETE "https://api.qoder.com.cn/api/v1/cloud/sessions/$SESSION_ID" \
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN"
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.cn/api/v1/cloud"
SESSION_ID="sess_019e5ce0bf9074b69c3481e93771a522"
HEADERS=(
  -H "Authorization: Bearer $QODER_ACCESS_TOKEN"
)

# 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 PAT or SAT has the required permissions for the target workspace. Environment access is scoped to the authenticated subject's permissions.

API Reference