From 2434f0d9a245c2c28060384a9a8be7669afe980b Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Mon, 3 Aug 2026 14:35:53 -0500 Subject: [PATCH] docs(chat): reconcile the resume-stream contract with what the route returns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three drifts found by live testing of recoupable/api#809, all published today. 1. ChatStreamErrorResponse declared `{ status, message }` with `message` required. Every 4xx on this route actually returns `error`, and validation failures add `missing_fields`. Observed on preview: {"status":"error","missing_fields":["startIndex"], "error":"startIndex must be a non-negative integer"} {"status":"error","error":"Forbidden"} The schema is pre-existing and backs 401/403/404 as well as the 400, so every documented error body on this endpoint was wrong. 2. The `account_id` query override shipped in api#809 and was undocumented. Documented with the authorisation rule, since the interesting part is that it is validated rather than trusted — a personal key passing someone else's id gets 403. 3. `x-workflow-stream-tail-index` ships on the 200 and was undocumented. The tail-index description states the semantic that cost a round of rework downstream: headers are sent before the body, so the value is the tail when the read OPENED, not where the response ended. A read advertising a tail of 9 went on to deliver 22 chunks. Documented as a base for computing absolute positions, not a resume point. Verified: file parses; params resolve to [chatId(path), account_id(query), startIndex(query)]; 200 headers to both; error schema requires [status, error]. Applied via anchored edits — research.json does not round-trip through json.dumps byte-for-byte. Refs recoupable/chat#1923 Co-Authored-By: Claude Opus 5 (1M context) --- api-reference/openapi/research.json | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/api-reference/openapi/research.json b/api-reference/openapi/research.json index 270cb41..c40e0d0 100644 --- a/api-reference/openapi/research.json +++ b/api-reference/openapi/research.json @@ -786,6 +786,16 @@ "format": "uuid" } }, + { + "name": "account_id", + "in": "query", + "required": false, + "description": "Act on a chat belonging to another account. Only honoured for a key that is authorised for that account — an organization key may pass a member account's id; a personal key passing anyone else's is rejected with `403 Access denied to specified account_id`. Omit it to act as the authenticated account.", + "schema": { + "type": "string", + "format": "uuid" + } + }, { "name": "startIndex", "in": "query", @@ -806,6 +816,12 @@ "schema": { "type": "string" } + }, + "x-workflow-stream-tail-index": { + "description": "Zero-based index of the last chunk known to the stream **at the moment this read was opened**. Because headers are sent before the body, a read that stays open past that point will deliver chunks beyond it — so this is a base for computing absolute positions, not a record of where the response ended. A client resuming precisely should count the chunks it receives on top of this value. Omitted if the runtime cannot report it.", + "schema": { + "type": "integer" + } } }, "content": { @@ -3221,7 +3237,7 @@ "type": "object", "required": [ "status", - "message" + "error" ], "properties": { "status": { @@ -3231,9 +3247,16 @@ ], "description": "Status of the request" }, - "message": { + "error": { "type": "string", "description": "Error message describing what went wrong" + }, + "missing_fields": { + "type": "array", + "description": "Present on validation failures (400): the path of the field that failed. Empty for a malformed path parameter.", + "items": { + "type": "string" + } } } },