Package io.getstream

Class Webhook

java.lang.Object
io.getstream.Webhook

public class Webhook extends Object
Webhook utilities for Stream webhooks.
  • Constructor Details

    • Webhook

      public Webhook()
  • Method Details

    • getEventType

      public static String getEventType(byte[] rawEvent)
      Extract the event type from a raw webhook payload.
      Parameters:
      rawEvent - The raw webhook payload as a byte array
      Returns:
      The event type string (e.g., "message.new"), or null if not found
    • getEventType

      public static String getEventType(String rawEvent)
      Extract the event type from a raw webhook payload.
      Parameters:
      rawEvent - The raw webhook payload as a string
      Returns:
      The event type string (e.g., "message.new"), or null if not found
    • parseWebhookEvent

      public static Object parseWebhookEvent(byte[] rawEvent) throws Webhook.InvalidWebhookError
      Deserialize a raw webhook payload into a typed event object.
      Parameters:
      rawEvent - The raw webhook payload as a byte array
      Returns:
      A typed event object corresponding to the event type
      Throws:
      Webhook.InvalidWebhookError - if the event type is unknown or deserialization fails
    • parseWebhookEvent

      public static Object parseWebhookEvent(String rawEvent) throws Webhook.InvalidWebhookError
      Deserialize a raw webhook payload into a typed event object.
      Parameters:
      rawEvent - The raw webhook payload as a string
      Returns:
      A typed event object corresponding to the event type
      Throws:
      Webhook.InvalidWebhookError - if the event type is unknown or deserialization fails
    • verifySignature

      public static boolean verifySignature(byte[] body, String signature, String secret)
      Verify the HMAC-SHA256 signature of a webhook payload.
      Parameters:
      body - The raw request body as a byte array
      signature - The signature from the X-Signature header
      secret - Your webhook secret (found in the Stream Dashboard)
      Returns:
      true if the signature is valid, false otherwise
    • verifySignature

      public static boolean verifySignature(String body, String signature, String secret)
      Verify the HMAC-SHA256 signature of a webhook payload.
      Parameters:
      body - The raw request body as a string
      signature - The signature from the X-Signature header
      secret - Your webhook secret (found in the Stream Dashboard)
      Returns:
      true if the signature is valid, false otherwise
    • gunzipPayload

      public static byte[] gunzipPayload(byte[] body) throws Webhook.InvalidWebhookError
      Decompress the body if it is gzip-prefixed, otherwise return it as-is.

      Detection uses the first two bytes (0x1F 0x8B). This is reliable because Stream webhook bodies are always JSON, and JSON cannot start with 0x1F.

      Parameters:
      body - the raw request body, possibly gzip-compressed
      Returns:
      the uncompressed bytes (or body unchanged if no gzip prefix)
      Throws:
      Webhook.InvalidWebhookError - if the body has the gzip magic prefix but isn't a valid gzip stream
    • decodeSqsPayload

      public static byte[] decodeSqsPayload(String messageBody) throws Webhook.InvalidWebhookError
      Decode an SQS Message Body: try base64 first, fall back to raw bytes if base64 fails, then gunzip if gzip-prefixed.

      Wire format (per CHA-3071): SQS bodies are raw JSON when enable_hook_payload_compression is off (today's default for all existing apps), and base64(gzip(json)) when it's on. This helper handles both: raw JSON starts with '{' which is not valid base64, so the base64 decode fails and we fall through to raw bytes, then gunzipPayload(byte[])'s magic-byte detection decides whether to decompress.

      parseSqs(String) sits on top of this and works transparently for both wire formats: no caller code change, no flag, no header.

      Throws:
      Webhook.InvalidWebhookError - if gzip decompression fails (only when input has gzip magic prefix)
    • decodeSnsPayload

      public static byte[] decodeSnsPayload(String notificationBody) throws Webhook.InvalidWebhookError
      Decode an SNS notification body. Accepts either:
      • a full SNS HTTP notification envelope JSON ( {"Type":"Notification","Message":"<base64>",...}), or
      • a pre-extracted Message string (forwarded-through-SQS path).
      The inner payload is then base64-decoded and gunzipped via decodeSqsPayload(String).
      Throws:
      Webhook.InvalidWebhookError
    • parseEvent

      public static Object parseEvent(byte[] payload) throws Webhook.InvalidWebhookError
      Parse a webhook payload and return the typed event for known discriminators or Webhook.UnknownEvent for well-formed-but-unknown ones.

      Distinct from parseWebhookEvent(byte[]): parseEvent returns an UnknownEvent on unrecognized discriminators (forward-compat); parseWebhookEvent throws.

      Returns:
      the typed event (a generated event class) or an Webhook.UnknownEvent instance
      Throws:
      Webhook.InvalidWebhookError - for invalid JSON, missing/non-string type field, or any deserialization failure on a known type
    • verifyAndParseWebhook

      public static Object verifyAndParseWebhook(byte[] body, String signature, String secret) throws Webhook.InvalidWebhookError
      HTTP composite: gunzip (if gzip-prefixed) then verify HMAC-SHA256 then parse.

      The signature header is X-Signature. The signature is HMAC-SHA256 of the uncompressed JSON body, hex-encoded. Magic-byte detection means callers can pass either the raw HTTP body or already-decompressed bytes; both work.

      Throws:
      Webhook.InvalidWebhookError - for every failure mode: signature mismatch, gunzip failure, JSON parse, type dispatch, or event deserialization failure. Filter on the message text (or the failure-mode constants on Webhook.InvalidWebhookError) to differentiate modes.
    • parseSqs

      public static Object parseSqs(String messageBody) throws Webhook.InvalidWebhookError
      SQS composite: base64-decode then gunzip (if gzip-prefixed) then parse.

      The backend emits no signature attribute on SQS messages today; this helper therefore performs no signature verification. If a signed variant is added later, it'll be a separate function rather than retrofitting this signature.

      Throws:
      Webhook.InvalidWebhookError
    • parseSns

      public static Object parseSns(String notificationBody) throws Webhook.InvalidWebhookError
      SNS composite: parse SNS envelope then base64-decode then gunzip then parse. Same no-signature posture as parseSqs(String).
      Throws:
      Webhook.InvalidWebhookError