You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(webhooks): align compression section with the shipped API
The previous draft referenced helpers that were renamed during the
refactor to the verify_and_parse_* contract (CHA-3071):
- client.verify_and_decode_webhook(...) -> verify_and_parse_webhook
- decompress_webhook_body(...) -> removed (no public form)
- content_encoding / payload_encoding -> removed (magic-byte detect)
Following the old snippets would hit AttributeError immediately. The
section is rewritten to document the real surface area:
- client.verify_and_parse_webhook(body, signature)
- client.verify_and_parse_sqs(message_body, signature)
- client.verify_and_parse_sns(message, signature)
- module-level webhook.verify_and_parse_* helpers for stateless use
- WebhookSignatureError as the single error class
It also clarifies the return type (parsed dict, not raw bytes) and
notes that the legacy verify_webhook bool helper stays unchanged.
Co-authored-by: Cursor <cursoragent@cursor.com>
GZIP compression can be enabled for hooks payloads from the Dashboard. Enabling compression reduces the payload size significantly (often 70–90% smaller) reducing your bandwidth usage on Stream. The computation overhead introduced by the decompression step is usually negligible and offset by the much smaller payload.
95
+
GZIP compression can be enabled for hook payloads from the Dashboard. Enabling compression reduces the payload size significantly (often 70–90% smaller) reducing your bandwidth usage on Stream. The decompression cost on your side is usually negligible and offset by the much smaller payload.
96
96
97
-
When payload compression is enabled, webhook HTTP requests will include the `Content-Encoding: gzip` header and the request body will be compressed with GZIP. Some HTTP servers and middleware (Rails, Django, Laravel, Spring Boot, ASP.NET) handle this transparently and strip the header before your handler runs — in that case the body you see is already raw JSON.
97
+
When payload compression is enabled, webhook HTTP requests include the `Content-Encoding: gzip` header and the body is gzipped. SQS and SNS messages are gzipped and then base64-wrapped (both transports are UTF-8 only). Some HTTP servers and middleware (Rails, Django, Laravel, Spring Boot, ASP.NET) auto-decompress the body before your handler runs — in that case the body you see is already raw JSON.
98
98
99
99
Before enabling compression, make sure that:
100
100
101
101
* Your backend integration is using a recent version of our official SDKs with compression support
102
102
* If you don't use an official SDK, make sure that your code supports receiving compressed payloads
103
103
* The payload signature check is done on the **uncompressed** payload
104
104
105
-
The Python SDK ships with `client.verify_and_decode_webhook(...)` which transparently handles plain, gzip-compressed, and base64-wrapped (SQS / SNS firehose) payloads. It returns the raw JSON body as `bytes`, ready to pass to `json.loads`.
105
+
The Python SDK exposes a one-liner per transport. Each helper detects the encoding from the body bytes (the gzip magic `1f 8b`, per [RFC 1952](https://datatracker.ietf.org/doc/html/rfc1952)), verifies the HMAC `X-Signature` over the uncompressed JSON, and returns the parsed event as a `dict`. Typed event classes are planned for a future release; until then handlers can key off the `type` field.
If your HTTP framework or a middleware already decompressed the body before it reached your handler, the `Content-Encoding` header will be missing (or set to `identity`) and `verify_and_decode_webhook` will be a no-op for the decompression step — the same call works in both cases.
136
+
The same call works whether or not Stream is compressing for this app, and whether or not your framework auto-decompressed the request — the helper inspects the body bytes rather than the `Content-Encoding` header.
143
137
144
-
`verify_and_decode_webhook` raises `stream_chat.base.exceptions.WebhookSignatureError` when the signature does not matchor the body cannot be decoded.
138
+
All helpers raise `stream_chat.base.exceptions.WebhookSignatureError` when the signature does not match, when the gzip stream is corrupt, or when the SQS/SNS base64 envelope cannot be decoded.
145
139
146
-
The original `client.verify_webhook(request.body, request.headers["X-Signature"])`is unchanged and still available for handlers that prefer to verify and parse the body separately.
140
+
The original `client.verify_webhook(request.body, request.headers["X-Signature"])`— which returns a `bool`and does not decompress — stays unchanged for backward compatibility. Switch to `verify_and_parse_webhook` to support compressed payloads.
147
141
148
142
#### SQS / SNS firehose
149
143
150
-
When delivering events through SQS or SNS, Stream base64-wraps the (possibly gzip-compressed) body so the payload stays valid UTF-8 over the queue. Pass `payload_encoding="base64"` so `verify_and_decode_webhook` unwraps the envelope before verifying the HMAC signature, which is always computed over the uncompressed JSON.
144
+
For events delivered through SQS or SNS, call the matching helper on the message body. It base64-decodes the envelope, gzip-decompresses when the magic bytes are present, verifies the HMAC, and returns the parsed event.
If you do not want to construct a `StreamChat` client (for example in a lightweight Lambda that only handles webhooks), call the module-level helpers directly. They take the API secret as a third argument and are otherwise identical:
If you only need to decode the body without checking the signature (for example because you have already verified it elsewhere), use `client.decompress_webhook_body(body, content_encoding, payload_encoding)`.
170
+
The module also exposes the primitives the composites are built from — `ungzip_payload`, `decode_sqs_payload`, `decode_sns_payload`, `verify_signature` (constant-time HMAC-SHA256), and `parse_event` — for callers that need to run the steps individually.
0 commit comments