The core rule
When you are not sure whether something happened, look it up instead of repeating it.
Store session IDs in your own database as soon as you get them. They are your handle on everything else.
Retry safely with idempotency keys
An idempotency key is a unique string you attach to a request in theIdempotency-Key header. If you send the same request with the same key again, Orca recognizes it and returns the original result instead of doing the work twice.
- Reuse the key only for the identical request. A different body with the same key is rejected.
- Make a new key for a new action, never to get past an error.
- If the original request is still being processed, or was interrupted, a retry returns HTTP 409 instead of a result. Look up the state (see the table above) before deciding what to do.
max_retries=0on the client turns off automatic retries so that your own code decides when to retry. It does not by itself prevent duplicates; idempotency keys do.
After a stream disconnects
Closing or losing a stream does not affect the turn.- Call
sessions.retrieve(session_id)andsessions.turns.list(session_id). - If the turn ended, read the result. You are done.
- If not, reconnect with
sessions.events.stream(session_id). Raw SSE consumers can sendLast-Event-IDto replay missed events; ignore duplicate IDs.
When the session needs you
Ifrequired_actions is not empty:
function_call: send oneagent.session.input.tool_resultfor eachcall_id. Track which you answered, and do not rerun side effects for a call you already answered. See function tools.environment_connection: start or restart your self-hosted executor. See execution environments.
When the Orca server restarts
- Running turns are marked
failed. Orca does not re-run them, because the agent may already have changed things in the outside world. Check those systems, then send a new message if the work should continue. - Managed sandboxes reconnect to the same sandbox. If it no longer exists, Orca restores it from its last saved workspace, or reports an error. It never silently gives you an unrelated empty sandbox.
- History is intact. Every accepted message and every finished item is stored.
A bounded status loop
Never wait forever. This loop gives up after five minutes and leaves the turn running so you can inspect it:Cancel and clean up
- To stop work, send
{"type": "agent.session.input.cancel"}withsessions.events.create. Closing a stream does not stop anything. - Delete sessions you no longer need; this releases their sandbox. Download artifacts first.
- Delete other resources separately: agents, files, skills, templates, and vaults are not deleted with sessions.
- Self-hosted executors are your processes. Stop them yourself.
Outcomes to handle separately
Keep API keys and sensitive tool results out of logs and telemetry. See also: streaming, runnable examples, and the errors reference.