From f3206bc1e62330d3011d609dbaa116475470e7a8 Mon Sep 17 00:00:00 2001 From: Sivert Date: Tue, 15 Sep 2026 12:13:25 +0200 Subject: [PATCH] Webhooks page: bad JSON is a 400 and big bodies a 413 now (GRYT-1198) Server#188 made the send route answer malformed JSON with 400 invalid_json and a body over 256 KB with 413 body_too_large. Until then both were 500s, and the page warned about that. The vendored openapi.json is copied from server main. The error table gets both rows, and the warning becomes a line about older servers. Rate limits now say those two are refused before they're counted. Co-Authored-By: Claude Opus 5 --- content/docs/build/webhooks.mdx | 18 ++++++++---------- src/components/webhooks/openapi.json | 26 ++++++++++++++++++++------ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/content/docs/build/webhooks.mdx b/content/docs/build/webhooks.mdx index b0aff00..67ddcdd 100644 --- a/content/docs/build/webhooks.mdx +++ b/content/docs/build/webhooks.mdx @@ -4,7 +4,6 @@ description: Post messages and cards into a Gryt channel with one HTTP request, icon: Webhook --- -import { Callout } from 'fumadocs-ui/components/callout'; import { WebhookPreview } from '@/components/webhooks/preview-loader'; import { PayloadTable, CodeTable } from '@/components/webhooks/reference'; @@ -211,15 +210,14 @@ problem: | 400 | `{"error": "empty_message"}` | No `text` (or only spaces) and no cards | | 400 | `{"error": "message_too_long"}` | `text` is over 4,000 characters | | 400 | `{"error": "no_channel"}` | The webhook has no channel set | +| 400 | `{"error": "invalid_json"}` | The body isn't valid JSON | | 404 | `{"error": "not_found"}` | No webhook with that ID and token | +| 413 | `{"error": "body_too_large"}` | The body is over 256 KB | | 429 | `{"error": "rate_limited", "retry_after_ms": 59988}` | See [Rate limits](#rate-limits) | - -JSON that doesn't parse gets a `500` with `"error": "internal_error"` and the -parser's message today. So does a body over 2 MB. Both should really be 4xx -answers and will probably change. Don't match on them. Treat a 5xx as "check -the request, then try again later". - +Older servers answer bad JSON with a `500` and `"error": "internal_error"` +instead, and let bodies up to 2 MB through before doing the same. If you have to +handle those, treat a 5xx as "check the request, then try again later". ### Warnings @@ -248,9 +246,9 @@ Each webhook gets a burst of 15 requests, then about one every 2 seconds, or 30 a minute. Go over and that webhook gets `429` for 60 seconds, with `retry_after_ms` saying how long is left. -It counts every request to the webhook's URL, including refused ones, and it's -per webhook rather than per caller, so two scripts sharing a URL share the -limit. The counter lives in the server's memory and resets when it restarts. +It counts every request to the webhook's URL, including refused payloads, and +it's per webhook rather than per caller, so two scripts sharing a URL share the +limit. Bad JSON and bodies over 256 KB are turned away before they're counted. The counter lives in the server's memory and resets when it restarts. [Rate limiting](/docs/host/rate-limiting) has the server's other limits. ## Recipes diff --git a/src/components/webhooks/openapi.json b/src/components/webhooks/openapi.json index d98bea2..f22843e 100644 --- a/src/components/webhooks/openapi.json +++ b/src/components/webhooks/openapi.json @@ -438,7 +438,7 @@ } }, "400": { - "description": "The payload was refused, or the webhook has no channel. Nothing was posted.", + "description": "The body isn't valid JSON (`invalid_json`), the payload was refused, or the webhook has no channel. Nothing was posted.", "content": { "application/json": { "schema": { @@ -465,7 +465,7 @@ } }, "413": { - "description": "The body is over 256 KB.", + "description": "The body is over 256 KB (`body_too_large`).", "content": { "application/json": { "schema": { @@ -577,11 +577,18 @@ } }, "400": { - "description": "The body was refused.", + "description": "The body was refused, or isn't valid JSON (`invalid_json`).", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InvalidPayload" + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidPayload" + }, + { + "$ref": "#/components/schemas/Error" + } + ] } } } @@ -703,11 +710,18 @@ } }, "400": { - "description": "The body was refused.", + "description": "The body was refused, or isn't valid JSON (`invalid_json`).", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InvalidPayload" + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidPayload" + }, + { + "$ref": "#/components/schemas/Error" + } + ] } } }