> This page is also available as Markdown: append .md to its URL. The full index of Sikaru docs is at https://docs.sikaru.ai/llms.txt. Code examples use only operations from the public API reference at https://docs.sikaru.ai/api-reference.

# Sessions and steering

Operations on this page (Python SDK `sikaru_api`; `client` and `project` from the Quickstart):

1. `client.execution_sessions.create(project, agent_slug, tenant_id=..., user_id=..., reasoning_effort=..., final_output_schema=...)["session"]` creates a session. `reasoning_effort`: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`.
2. `client.execution_sessions.append_turn(project, session_id, idempotency_key=..., input={"text": ...})` steers active work (default). Add `delivery_mode="queue"` for an independent follow-up.
3. `client.execution_sessions.list_session_inputs(project, session_id)` lists input receipts and how each was delivered.
4. Observe the resulting run with the Runs operations. An accepted turn is not the final answer.

Rules: one stable idempotency key per logical input; a session pins its release and model; map sessions to authenticated users.

Use an **execution session** when a user needs to continue a conversation. Keep its ID in your application and append later messages to that session. Creating a new session starts separate context; it does not continue the last run automatically.

These examples use the authenticated `client` and `project` from the [Quickstart](/get-started/quickstart).

## Steer or queue input

**Steering** is the default for active work: the input joins the turn already running. **Queued** input (`delivery_mode="queue"`) creates an independent follow-up.

```mermaid
sequenceDiagram
    participant App as Your application
    participant Session
    App->>Session: append_turn "Analyze this case"
    Note over Session: Turn running
    App->>Session: append_turn "Focus on renewal risk" (steer, default)
    Note over Session: Steering joins the active turn
    App->>Session: append_turn "Explain the findings" (delivery_mode="queue")
    Note over Session: Queued input runs as a follow-up
    App->>Session: list_session_inputs
    Session-->>App: Input receipts and delivery
```

The first message starts a turn. A steering message changes that active turn. A queued message waits and runs as its own follow-up. `list_session_inputs` shows how each input was delivered.

```python
session = client.execution_sessions.create(
    project, "support", tenant_id="customer-42", user_id="user-7",
)["session"]
turn = client.execution_sessions.append_turn(
    project, session["id"], idempotency_key="case-42-analysis",
    input={"text": "Analyze this case"},
)
steering = client.execution_sessions.append_turn(
    project, session["id"], idempotency_key="case-42-focus",
    input={"text": "Focus on renewal risk"},
)
queued = client.execution_sessions.append_turn(
    project, session["id"], idempotency_key="case-42-explanation",
    input={"text": "Explain the findings"}, delivery_mode="queue",
)
receipts = client.execution_sessions.list_session_inputs(project, session["id"])
```

> **Tip**
>
> Keep input receipt IDs because late steering can move to a follow-up run.

Session responses are dictionaries where the API declares open objects; run responses such as `runs.start` use generated models. A session preserves context across turns. Keep its ID to continue the conversation.

## Set structured output and reasoning effort

Set these controls when creating a session:

```python
session = client.execution_sessions.create(
    project, "support", tenant_id="customer-42", user_id="user-7",
    reasoning_effort="medium",
    final_output_schema={
        "type": "object",
        "properties": {"summary": {"type": "string"}},
        "required": ["summary"],
        "additionalProperties": False,
    },
)["session"]
```

| Control               | Behavior                                                        |
| --------------------- | --------------------------------------------------------------- |
| `final_output_schema` | The API validates the output schema before admission.           |
| `reasoning_effort`    | Accepts `none`, `minimal`, `low`, `medium`, `high`, or `xhigh`. |

These controls do not select a model vendor. Inspect the run's final output and terminal status rather than treating intermediate events as the schema-constrained result.

To explore another approach from a completed turn, see [Session branching](/run/session-branching). For recurring input, see [Schedules](/run/schedules).

## Read delivery and progress

The append response records admission of an input. Keep its receipt and inspect `list_session_inputs` to determine how it was delivered, especially when steering arrives near completion. Observe the associated run using [Runs and tool requests](/run/runs-and-tool-requests).

> **Note**
>
> An accepted turn is not the final answer.

* Use a stable idempotency key per logical input. Retrying the same input should preserve its key and payload; a genuinely new message needs a new key.
* Map sessions to your authenticated application users so one user cannot continue another user's conversation by supplying its ID.
* A session pins its release and model. Start a new session to use a newly active release.
* Branch only when you want a separate conversation based on an eligible saved snapshot.

## Next steps

#### [Runs and tool requests](/run/runs-and-tool-requests)

Observe the run behind each turn and resolve pending actions.

#### [Session branching](/run/session-branching)

Explore another approach from a completed turn.

#### [Schedules](/run/schedules)

Append recurring input to a session.