mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
docs(api): declare the 429 headers instead of only describing them
The shared 429 named `Retry-After` and the `X-RateLimit` triple in prose, so a generated client got no typed accessor for the field the same sentence tells it to back off on. They are a `headers` block now: `Retry-After` and the cap and remainder as integers, the reset instant as a date-time. The text also says what a 429 from a ceiling that is not one of these buckets carries — nothing, because there is no bucket to describe. Two route descriptions still listed the pre-change header set: the ingest 429 named only `X-RateLimit-Remaining` / `X-RateLimit-Reset`, and the document suggest 429 named only the reset instant beside `meta.retryAt`. Both are short of the wire in exactly the direction this work exists to close.
This commit is contained in:
1 parent
42100d94a7
commit
cf9e8b8790
4 files changed
+57
-12
No files matched your search
+32
-9
@@ -112,10 +112,32 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"429": &a5
|
||||
description: Rate limit exceeded. The response carries `Retry-After` (whole seconds, rounded up — wait at least that
|
||||
long before retrying), `X-RateLimit-Limit` (the bucket's cap), `X-RateLimit-Remaining` and
|
||||
description: Rate limit exceeded. The response carries `Retry-After` (whole seconds, rounded up and never below 1 — wait
|
||||
at least that long before retrying), `X-RateLimit-Limit` (the bucket's cap), `X-RateLimit-Remaining` and
|
||||
`X-RateLimit-Reset` (the reset instant, ISO-8601, not epoch seconds). Back off on `Retry-After` rather than
|
||||
guessing.
|
||||
guessing. A 429 raised by a ceiling that is not one of these buckets — a daily AI budget, an upstream
|
||||
provider's own refusal relayed onward — carries none of them, because there is no bucket to describe.
|
||||
headers:
|
||||
Retry-After:
|
||||
description: Whole seconds to wait before retrying. Rounded up, never below 1.
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
X-RateLimit-Limit:
|
||||
description: Requests the bucket allows per window.
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
X-RateLimit-Remaining:
|
||||
description: Requests left in the current window; 0 on a refusal.
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 0
|
||||
X-RateLimit-Reset:
|
||||
description: Instant the current window rolls over, ISO-8601 — not epoch seconds.
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -15432,9 +15454,10 @@ paths:
|
||||
returns a `{ title, kind, documentDate }` DRAFT for the edit form. AI-consent / rate / budget gated (shares the
|
||||
per-user document-AI bucket with extract/summary/index — default 6/hour, operator-tunable via
|
||||
`DOCUMENT_AI_LIMIT_PER_HOUR`; a slot is only consumed when the request reaches the provider, and the 429 carries
|
||||
the reset instant in `X-RateLimit-Reset` and `meta.retryAt`). WRITES NOTHING — never stages facts, never flips
|
||||
status; the user reviews and saves. 422 (`documents.inbound.providerUnsupported`) with no provider configured.
|
||||
Never interprets or diagnoses; the title is a neutral filing label."
|
||||
`Retry-After`, the `X-RateLimit-Limit` / `-Remaining` / `-Reset` triple, and `meta.retryAt`). WRITES NOTHING —
|
||||
never stages facts, never flips status; the user reviews and saves. 422
|
||||
(`documents.inbound.providerUnsupported`) with no provider configured. Never interprets or diagnoses; the title
|
||||
is a neutral filing label."
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
@@ -19768,9 +19791,9 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"429":
|
||||
description: More than 60 requests in a minute for this token — or, when no valid token was presented, from this client
|
||||
IP. `meta.errorCode` = `ingest.rate_limited`, plus the standard `X-RateLimit-Remaining` /
|
||||
`X-RateLimit-Reset` headers.
|
||||
description: "More than 60 requests in a minute for this token — or, when no valid token was presented, from this client
|
||||
IP. `meta.errorCode` = `ingest.rate_limited`, plus the standard rate-limit headers: `Retry-After` (whole
|
||||
seconds, at least 1), `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`."
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
|
||||
@@ -793,7 +793,7 @@ export const inboundDocumentPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
tags: ["Documents"],
|
||||
summary: "Suggest filing metadata (drafts only)",
|
||||
description:
|
||||
'Optional AI assist on an already-stored document. Runs ONE provider call over the stored original (VISION, empty body) or browser-OCR\'d text (TEXT, `application/json` `{ mode: "text", text }`, opt-in local OCR) and returns a `{ title, kind, documentDate }` DRAFT for the edit form. AI-consent / rate / budget gated (shares the per-user document-AI bucket with extract/summary/index — default 6/hour, operator-tunable via `DOCUMENT_AI_LIMIT_PER_HOUR`; a slot is only consumed when the request reaches the provider, and the 429 carries the reset instant in `X-RateLimit-Reset` and `meta.retryAt`). WRITES NOTHING — never stages facts, never flips status; the user reviews and saves. 422 (`documents.inbound.providerUnsupported`) with no provider configured. Never interprets or diagnoses; the title is a neutral filing label.',
|
||||
'Optional AI assist on an already-stored document. Runs ONE provider call over the stored original (VISION, empty body) or browser-OCR\'d text (TEXT, `application/json` `{ mode: "text", text }`, opt-in local OCR) and returns a `{ title, kind, documentDate }` DRAFT for the edit form. AI-consent / rate / budget gated (shares the per-user document-AI bucket with extract/summary/index — default 6/hour, operator-tunable via `DOCUMENT_AI_LIMIT_PER_HOUR`; a slot is only consumed when the request reaches the provider, and the 429 carries `Retry-After`, the `X-RateLimit-Limit` / `-Remaining` / `-Reset` triple, and `meta.retryAt`). WRITES NOTHING — never stages facts, never flips status; the user reviews and saves. 422 (`documents.inbound.providerUnsupported`) with no provider configured. Never interprets or diagnoses; the title is a neutral filing label.',
|
||||
parameters: [
|
||||
{ name: "id", in: "path", required: true, schema: { type: "string" } },
|
||||
],
|
||||
|
||||
@@ -92,7 +92,7 @@ export const ingestPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
},
|
||||
"429": {
|
||||
description:
|
||||
"More than 60 requests in a minute for this token — or, when no valid token was presented, from this client IP. `meta.errorCode` = `ingest.rate_limited`, plus the standard `X-RateLimit-Remaining` / `X-RateLimit-Reset` headers.",
|
||||
"More than 60 requests in a minute for this token — or, when no valid token was presented, from this client IP. `meta.errorCode` = `ingest.rate_limited`, plus the standard rate-limit headers: `Retry-After` (whole seconds, at least 1), `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`.",
|
||||
content: { "application/json": { schema: errorEnvelope } },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -239,8 +239,30 @@ export const stdResponses = {
|
||||
},
|
||||
"429": {
|
||||
description:
|
||||
"Rate limit exceeded. The response carries `Retry-After` (whole seconds, rounded up — wait at least that long before retrying), `X-RateLimit-Limit` (the bucket's cap), `X-RateLimit-Remaining` and `X-RateLimit-Reset` (the reset instant, ISO-8601, not epoch seconds). Back off on `Retry-After` rather than guessing.",
|
||||
"Rate limit exceeded. The response carries `Retry-After` (whole seconds, rounded up and never below 1 — wait at least that long before retrying), `X-RateLimit-Limit` (the bucket's cap), `X-RateLimit-Remaining` and `X-RateLimit-Reset` (the reset instant, ISO-8601, not epoch seconds). Back off on `Retry-After` rather than guessing. A 429 raised by a ceiling that is not one of these buckets — a daily AI budget, an upstream provider's own refusal relayed onward — carries none of them, because there is no bucket to describe.",
|
||||
content: { "application/json": { schema: errorEnvelope } },
|
||||
// Declared rather than only described: a generated client gets a typed
|
||||
// accessor for the field the description tells it to back off on.
|
||||
headers: {
|
||||
"Retry-After": {
|
||||
description:
|
||||
"Whole seconds to wait before retrying. Rounded up, never below 1.",
|
||||
schema: { type: "integer" as const, minimum: 1 },
|
||||
},
|
||||
"X-RateLimit-Limit": {
|
||||
description: "Requests the bucket allows per window.",
|
||||
schema: { type: "integer" as const, minimum: 1 },
|
||||
},
|
||||
"X-RateLimit-Remaining": {
|
||||
description: "Requests left in the current window; 0 on a refusal.",
|
||||
schema: { type: "integer" as const, minimum: 0 },
|
||||
},
|
||||
"X-RateLimit-Reset": {
|
||||
description:
|
||||
"Instant the current window rolls over, ISO-8601 — not epoch seconds.",
|
||||
schema: { type: "string" as const, format: "date-time" },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user