diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index f53659552..c8049282e 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -19188,6 +19188,13 @@ paths: $ref: "#/components/schemas/DocumentUploadEnvelope" "401": *a1 "403": *a9 + "404": + description: An `episodeIds` or `encounterIds` id is not a live episode or visit of the caller + (`documents.inbound.episodeNotFound` / `documents.inbound.encounterNotFound`). Nothing was stored. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" "409": description: The same bytes are already stored under 20 other import source ids (`documents.inbound.sourceAliasLimit`). Nothing was stored or remembered. @@ -78996,6 +79003,15 @@ components: properties: requestId: type: string + duplicate: + description: "Present on a 200: the bytes or the source key were already stored, and `data` is the existing row (or the + receipt)." + type: boolean + const: true + deleted: + description: Present with `duplicate` when the source key belongs to a document the owner deleted. Nothing was stored. + type: boolean + const: true additionalProperties: false required: - data diff --git a/src/lib/openapi/routes/documents.ts b/src/lib/openapi/routes/documents.ts index 0808f1686..182a0cee2 100644 --- a/src/lib/openapi/routes/documents.ts +++ b/src/lib/openapi/routes/documents.ts @@ -379,11 +379,31 @@ const documentUploadReceipt = z }); // Shared by the POST 200 (duplicate) and 201 (created) responses — the -// envelope id must be registered exactly once. -const documentUploadEnvelope = dataEnvelope( - z.union([inboundDocument, documentUploadReceipt]), - "DocumentUploadEnvelope", -); +// envelope id must be registered exactly once. The duplicate answer flags +// itself at the envelope level, beside the request id. +const documentUploadEnvelope = z + .object({ + data: z.union([inboundDocument, documentUploadReceipt]), + error: z.null(), + meta: z + .object({ + requestId: z.string().optional(), + duplicate: z + .literal(true) + .optional() + .describe( + "Present on a 200: the bytes or the source key were already stored, and `data` is the existing row (or the receipt).", + ), + deleted: z + .literal(true) + .optional() + .describe( + "Present with `duplicate` when the source key belongs to a document the owner deleted. Nothing was stored.", + ), + }) + .optional(), + }) + .meta({ id: "DocumentUploadEnvelope" }); /** * A `DOCUMENT_EXTRACT` run's result. Smaller than the synchronous extract @@ -672,6 +692,11 @@ export const inboundDocumentPaths: NonNullable = { 'Unsupported or unidentifiable type (`meta.reason = "unsupportedType"`). Executables, HTML/SVG, and generic archives are always refused.', content: { "application/json": { schema: errorEnvelope } }, }, + "404": { + description: + "An `episodeIds` or `encounterIds` id is not a live episode or visit of the caller (`documents.inbound.episodeNotFound` / `documents.inbound.encounterNotFound`). Nothing was stored.", + content: { "application/json": { schema: errorEnvelope } }, + }, ...stdResponses, }, },