in_progress, completed, failed, incomplete, cancelled) are the only states the 2026-08-11 specification defines. HarnessRouter or other servers may have internal runner states; this page does not describe them, and the protocol does not require them. Conformance is a correctness check, not a lifecycle guarantee.1. Protocol version negotiation
UHP versions are dates (YYYY-MM-DD), the day the version was published. A client MAY declare the version it was written against with the UHP-Version request header.
- If the header is absent, the server MUST assume its own default version and MUST state that version in the response header.
- If the header names a supported version, the server MUST honour it for that request.
- If the header names an unsupported version, the server MUST fail with
400andcode: "unsupported_protocol_version", and the errordetailMUST list the versions it supports. It MUST NOT silently serve a different version. - Every response, including errors, MUST carry the version actually used in the
UHP-Versionheader.
2. Capability discovery
GET /v1/uhp is served without a bearer token so a client can confirm it is talking to a UHP server before presenting credentials. The document MUST NOT contain anything principal-specific.
The discovery object reports versions, default_version, conformance_class (core, extended, or full), and a capabilities object of named booleans (for example streaming, sessions, cancellation, files_input, files_output, session_listing, harness_management, session_sharing, idempotency).
- A server MUST report
falsefor a capability it does not implement, rather than omitting it, so a client can tell "not supported" from "server is older than this field". A client MUST treat an absent capability key asfalse. conformance_classMUST be consistent withcapabilities— for example, a server claimingextendedMUST reportfiles_input,files_output, andsession_listingastrue.
Reference-implementation note (not a protocol requirement): the discovery document MAY include a free-form implementation object (for example {"name":"HarnessRouter Community Edition","version":"0.3.0"}) for debugging. The protocol only requires object, protocol, versions, default_version, conformance_class, and capabilities.
3. Task and response lifecycle
A task is one unit of work submitted to POST /v1/responses (with Authorization: Bearer <token> and Content-Type: application/json). Key request fields: input (string or item array, required), model, metadata.harness_id (selects the configured harness), stream, previous_response_id, instructions, store, max_output_tokens, max_step, timeout_seconds, tools, include, background. The server MUST ignore unknown request fields rather than reject the request. The status field on the response object carries the current state.
The task state machine is small and well defined:
POST /v1/responses
│
▼
in_progress ──┬──▶ completed (terminal)
│ ├──▶ failed (terminal; error explains why)
│ ├──▶ incomplete (terminal; a budget stopped the work)
└────────┴──▶ cancelled (terminal; client cancelled)
| Status | Meaning | Terminal |
|---|---|---|
in_progress | Accepted and running. | no |
completed | The harness finished and produced a result. | yes |
failed | The task could not be completed; error explains why. | yes |
incomplete | The harness stopped at a budget (step or time limit) with partial output. | yes |
cancelled | The client cancelled it; partial output MAY be present. | yes |
Rules:
- A server MUST NOT transition out of a terminal state; later reads of a terminal response return the same terminal status.
incompleteMUST be used when a budget stopped the work, and MUST NOT be used for errors. The distinction matters:incompleteis usually worth continuing,failedusually is not.- A
cancelledtask MUST reportcancelled, notfailed. - Terminal responses MUST retain whatever output was produced before they became terminal.
4. Session creation and continuation
- A session is created by the server when the first task of a chain runs; its id MUST appear in the response's
metadata.session_id. - A session MUST preserve, across tasks in the chain: conversational context, the working directory and its files, and the configured harness.
- A task continues a session by sending
previous_response_id. Chaining on the response id (not the session id) names an exact point in the conversation.modelMAY differ between tasks; the server MUST honour a per-task model change. - A session MUST NOT be extended by a task naming a different configured harness. The server MUST fail with
409andcode: "harness_mismatch"rather than silently starting a new session. - Session expiry is server policy. On continuation, a server MUST report
404withcode: "session_expired"(distinguishable fromsession_not_found). An unknownprevious_response_idyields404response_not_found.
Listing, inspection, and sharing are class-gated: GET /v1/sessions and /turns are Extended; sharing (POST/GET /v1/sessions/{id}/share) is Full and MUST be read-only, revocable, and MUST NOT expose credentials or another principal's data.
5. Streaming behavior
With stream: true, the server responds with Content-Type: text/event-stream and emits Server-Sent Events whose data is one JSON object each. Rules:
- Every event MUST carry
typeandsequence_number;sequence_numberMUST start at0and increase by exactly 1 per event, so a client can detect a dropped event. - The stream MUST end with exactly one terminal event:
response.completed,response.incomplete, orresponse.failed. Each carries the complete final response object, so a client that missed intermediate events can rely on the terminal one alone. - The server MUST NOT buffer the stream to completion; it MUST send events as they happen.
Ordering guarantees the server MUST provide: response.created is first; exactly one terminal event is last; for any item, output_item.added precedes every event referring to it and output_item.done follows them all; sequence_number is strictly monotonic with no gaps. The server MUST NOT guarantee, and a client MUST NOT assume, that items complete in the order added, that text arrives in any particular chunk size, or that any optional event type appears at all.
A dropped connection MUST NOT abort the task; the work continues server-side. To follow it, a client re-reads GET /v1/responses/{response_id} (or the live feed GET /v1/harnesses/{harness_id}/events). A server MAY support SSE Last-Event-ID resumption, resuming after the given sequence_number without replaying seen events. Non-streaming returns a single response object at the terminal state with identical results to the stream. A client SHOULD treat the stream as an optimisation and the stored response as the source of truth.
6. Cancellation semantics
Cancellation has two deliberate scopes:
| Endpoint | Scope |
|---|---|
POST /v1/responses/{response_id}/cancel | Stop this task. |
POST /v1/sessions/{session_id}/cancel | Stop whatever is running in this session. |
- Cancellation is a request, not a guarantee of immediacy. A server MUST stop the work as soon as it can and MUST reach a terminal state.
- A cancelled task MUST end with
status: "cancelled", neverfailed. (On the stream this arrives as theresponse.failedterminal event carryingstatus: "cancelled"in the response object — thestatusfield, not the event name, is authoritative.) - Output produced before cancellation MUST be retained.
- Cancelling an already-terminal task MUST succeed and change nothing.
- Cancelling MUST NOT delete the session; the conversation remains continuable. A server SHOULD respond to cancel within one second even if the harness takes longer to wind down.
Deletion is the one place cancel and delete are coupled: DELETE /v1/traces/{session_id} MUST cancel any in-flight task first and then make the session unreadable. A bare DELETE /v1/responses/{response_id} deletes stored history but MUST NOT cancel a running task.
7. Incomplete versus failed work
Both are terminal, but the protocol distinguishes them because a client should treat them differently:
| Status | Trigger | Client guidance |
|---|---|---|
incomplete | A budget (max_step or timeout_seconds) stopped the work, with partial output retained. | Usually worth continuing (new task with previous_response_id). |
failed | The harness could not complete the work; error (and on the wire a timeout/harness_error/provider_error code) explains why. | Usually not worth retrying unchanged. |
max_step and timeout_seconds are budgets, not precise guarantees: a server MUST stop the task at or after the budget, MUST report incomplete, and MUST NOT report completed for truncated work. A task that reached failed is reported as a response with status: "failed" and HTTP 200 — the request succeeded even though the task did not; that is distinct from a non-2xx error envelope, which means the request itself failed.
8. Artifact and file handoff
Input files enter a task as inline input_file/input_image items (a data URL in file_data or a file_id from a prior upload); a Core server MUST accept input_text, an Extended server MUST also accept input_file and input_image. Output artifacts surface as annotations on message content; they are addressed by container_id / file_id and downloaded via GET /v1/containers/{container_id}/files/{file_id}/content, listed via GET /v1/sessions/{session_id}/files, and bulk-fetched via GET /v1/sessions/{session_id}/files/archive.
Because artifacts are attacker-influenced content, the normative handling rules (served with X-Content-Type-Options: nosniff, bounded to their container so traversal cannot escape, size/count caps) live in the security model; the conformance suite probes path traversal as a floor, not proof. The lifecycle concern is simply that partial output is retained through every terminal state and erased when the session is deleted.
9. Retries, continuation, and idempotency
These three interact but are distinct mechanisms:
- Continuation — sending
previous_response_idstarts a new task (a newresponse_id) that reuses the session's context, working directory, and harness. It is not a retry of the prior task. - Retry after failure — per the error rules:
server_error(500/502/503/504) is retryable with backoff;rate_limitedafterRetry-After;session_busyafter the in-flight task is terminal;invalid_request_errorandauthentication_errorare not. Afailedtask is only retried by the client's own judgment. - Idempotency — when the server advertises
idempotency, a repeatedIdempotency-KeyonPOST /v1/responsesMUST return the first result without starting a second execution; if the first is still running, the server waits for it. Keys SHOULD be retained for at least 24 hours. Retries ofPOST /v1/responsesMUST carry anIdempotency-Key, because a retry after a timeout otherwise runs the task twice.
Reference-implementation note: a second concurrent task in the same session is rejected with 409 session_busy; the server SHOULD include retry_after_ms in the error detail when it can estimate it. This prevents duplicate concurrent execution within a session.
10. What the specification does not define
To avoid inventing behavior, note explicitly what 2026-08-11 does not prescribe: there is no separate "queued" or "pending" state before in_progress; submission is either accepted (in_progress) or rejected with an error envelope. There is no resume-in-place of an interrupted task — recovery is a new task chained via previous_response_id. The protocol does not specify runner-internal states, exact timeout_seconds enforcement granularity, or cross-stream sequence_number continuity. HarnessRouter or other servers may add such mechanics; they are not UHP normative states.
Related pages
Read the HTTP API map, security and trust boundaries, conformance, architecture, the specification guide, governance and versioning, and the glossary.
Primary sources
- UHP 2026-08-11 Lifecycle (version negotiation, discovery, task/session states, concurrency)
- UHP 2026-08-11 Tasks (run a task, response object, idempotency, cancel, delete)
- UHP 2026-08-11 Sessions (continuation, listing, cancellation, sharing, delete)
- UHP 2026-08-11 Streaming (SSE, ordering, reconnect)
- UHP 2026-08-11 Errors (envelope, codes, retry rules, timeouts)
- UHP VERSIONING.md (date scheme, client retry rules)