---
description: Verify Capawesome Cloud webhook signatures to ensure payloads are authentic and untampered, with examples in Node.js and Python.
title: Securing Webhooks - Capawesome
image: https://capawesome.io/docs/assets/images/social/cloud/webhooks/securing.png
---

[ Skip to content](#securing-webhooks) 

[ 🔐 Introducing the **Capacitor Vault** plugin — store secrets behind biometrics or a device passcode.](/blog/announcing-the-capacitor-vault-plugin/) 

* [  SDKs ](/docs/sdks/)
* [  Formbricks ](/docs/sdks/capacitor/formbricks/)
* [  Geocoder ](/docs/sdks/capacitor/geocoder/)
* [  Google Sign-In ](/docs/sdks/capacitor/google-sign-in/)
* [  Grafana Faro ](/docs/sdks/capacitor/grafana-faro/)
* [  libSQL ](/docs/sdks/capacitor/libsql/)
* [  Live Update ](/docs/sdks/capacitor/live-update/)
* [  Managed Configurations ](/docs/sdks/capacitor/managed-configurations/)
* [  Media Session ](/docs/sdks/capacitor/media-session/)
* [  ML Kit ](/docs/sdks/capacitor/mlkit/)
* [  Navigation Bar ](/docs/sdks/capacitor/navigation-bar/)
* [  NFC ](/docs/sdks/capacitor/nfc/)
* [  OAuth ](/docs/sdks/capacitor/oauth/)
* [  Pedometer ](/docs/sdks/capacitor/pedometer/)
* [  Photo Editor ](/docs/sdks/capacitor/photo-editor/)
* [  PostHog ](/docs/sdks/capacitor/posthog/)
* [  Printer ](/docs/sdks/capacitor/printer/)
* [  Purchases ](/docs/sdks/capacitor/purchases/)
* [  RealtimeKit ](/docs/sdks/capacitor/realtimekit/)
* [  Screen Orientation ](/docs/sdks/capacitor/screen-orientation/)
* [  Screenshot ](/docs/sdks/capacitor/screenshot/)
* [  Secure Preferences ](/docs/sdks/capacitor/secure-preferences/)
* [  Speech Recognition ](/docs/sdks/capacitor/speech-recognition/)
* [  Speech Synthesis ](/docs/sdks/capacitor/speech-synthesis/)
* [  Share Target ](/docs/sdks/capacitor/share-target/)
* [  Square Mobile Payments ](/docs/sdks/capacitor/square-mobile-payments/)
* [  SQLite ](/docs/sdks/capacitor/sqlite/)
* [  Superwall ](/docs/sdks/capacitor/superwall/)
* [  Torch ](/docs/sdks/capacitor/torch/)
* [  Vault ](/docs/sdks/capacitor/vault/)
* [  Wifi ](/docs/sdks/capacitor/wifi/)
* [  Zip ](/docs/sdks/capacitor/zip/)
* [  Cordova ](/docs/sdks/cordova/)
* [  Cloud ](/docs/cloud/)
* [  Integrations ](/docs/cloud/live-updates/integrations/)
* Concepts
* Reference
* [  Troubleshooting ](/docs/cloud/live-updates/troubleshooting/)
* [  FAQ ](/docs/cloud/live-updates/faq/)
* [  Native Builds ](/docs/cloud/native-builds/)
* [  Set Up Environments ](/docs/cloud/native-builds/environments/)
* [  Overwrite Native Configurations ](/docs/cloud/native-builds/native-configurations/)
* [  Auto-Increment Build Numbers ](/docs/cloud/native-builds/auto-incrementing-build-numbers/)
* [  Configure the Web Build Script ](/docs/cloud/native-builds/web-build-script/)
* [  Build from a Monorepo ](/docs/cloud/native-builds/monorepo/)
* [  Use pnpm or Yarn ](/docs/cloud/native-builds/package-managers/)
* [  Install Private npm Packages ](/docs/cloud/native-builds/npm-private-registry/)
* [  Override the Java Version ](/docs/cloud/native-builds/override-java-version/)
* [  Custom iOS Provisioning Profiles ](/docs/cloud/native-builds/custom-ios-provisioning-profiles/)
* [  Build without Git ](/docs/cloud/native-builds/build-without-git/)
* [  Access Git Behind a Firewall ](/docs/cloud/native-builds/firewall-access/)
* [  Integrations ](/docs/cloud/native-builds/integrations/)
* Reference
* [  Troubleshooting ](/docs/cloud/native-builds/troubleshooting/)
* [  FAQ ](/docs/cloud/native-builds/faq/)
* [  App Store Publishing ](/docs/cloud/app-store-publishing/)
* [  Submit a Build ](/docs/cloud/app-store-publishing/submit-a-build/)
* [  Submit Automatically After a Build ](/docs/cloud/app-store-publishing/submit-automatically/)
* [  Troubleshooting ](/docs/cloud/app-store-publishing/troubleshooting/)
* [  FAQ ](/docs/cloud/app-store-publishing/faq/)
* [  Automations ](/docs/cloud/automations/)
* [  Reference ](/docs/cloud/automations/reference/)
* [  Troubleshooting ](/docs/cloud/automations/troubleshooting/)
* [  FAQ ](/docs/cloud/automations/faq/)
* [  Assist ](/docs/cloud/assist/)
* [  CLI ](/docs/cloud/cli/)
* APIs and SDKs
* [  Webhooks ](/docs/cloud/webhooks/)
* [  Integrations ](/docs/cloud/integrations/)
* Account
* [  Organization ](/docs/cloud/organizations/)
* [  Two-Factor Enforcement ](/docs/cloud/organizations/two-factor-authentication/)
* [  Audit Logs ](/docs/cloud/organizations/audit-logs/)
* [  Billing ](/docs/cloud/organizations/billing/)
* [  License Keys ](/docs/cloud/license-keys/)
* [  AI ](/docs/ai/)
* [  Insiders ](/docs/insiders/)
* [  Billing & Plans ](/docs/insiders/billing-and-plans/)
* [  FAQ ](/docs/insiders/faq/)
* [  License ](https://capawesome.io/legal/eula/)
* [  Support ](/docs/support/)
* [  Contributing ](/docs/contributing/)
* Contributing code
* [  Code of Conduct ](/docs/contributing/code-of-conduct/)
* [  Questions ](https://docs.github.com/en/discussions/collaborating-with-your-community-using-discussions/participating-in-a-discussion#creating-a-discussion)
* [  Blog ](/blog/)
* Categories

# Securing Webhooks[¶](#securing-webhooks "Permanent link")

Your webhook endpoint is a public URL, so anyone who discovers it could send it a forged request. Signing fixes that: when a webhook has a **signing secret** configured (available for the `Raw` format), Capawesome Cloud signs every request so your endpoint can confirm it's genuine.

You set the signing secret when you [create or edit the webhook](/docs/cloud/webhooks/setup/). With it in place, each request includes an `X-Signature` header containing an HMAC-SHA256 signature of the raw request body. Verify this signature on your endpoint to ensure the request is authentic and hasn't been tampered with.

Two details matter for the check to be correct:

* Always compute the HMAC over the **raw request body** — the exact bytes you received, not a re-serialized object, since reserializing can change whitespace or key order and break the signature.
* Use a **constant-time comparison** to avoid leaking information through timing.

Node.jsPython

`[](#%5F%5Fcodelineno-0-1)import crypto from "node:crypto";
[](#%5F%5Fcodelineno-0-2)
[](#%5F%5Fcodelineno-0-3)const secret = "SIGNING_SECRET";
[](#%5F%5Fcodelineno-0-4)const digest = Buffer.from(
[](#%5F%5Fcodelineno-0-5)  crypto.createHmac("sha256", secret).update(request.rawBody).digest("hex"),
[](#%5F%5Fcodelineno-0-6)  "utf8"
[](#%5F%5Fcodelineno-0-7));
[](#%5F%5Fcodelineno-0-8)const signature = Buffer.from(request.get("X-Signature") || "", "utf8");
[](#%5F%5Fcodelineno-0-9)
[](#%5F%5Fcodelineno-0-10)if (digest.length !== signature.length || !crypto.timingSafeEqual(digest, signature)) {
[](#%5F%5Fcodelineno-0-11)  throw new Error("Invalid signature.");
[](#%5F%5Fcodelineno-0-12)}
`

`[](#%5F%5Fcodelineno-1-1)import hashlib
[](#%5F%5Fcodelineno-1-2)import hmac
[](#%5F%5Fcodelineno-1-3)
[](#%5F%5Fcodelineno-1-4)secret = b"SIGNING_SECRET"
[](#%5F%5Fcodelineno-1-5)digest = hmac.new(secret, raw_body, hashlib.sha256).hexdigest()
[](#%5F%5Fcodelineno-1-6)signature = request.headers.get("X-Signature", "")
[](#%5F%5Fcodelineno-1-7)
[](#%5F%5Fcodelineno-1-8)if not hmac.compare_digest(digest, signature):
[](#%5F%5Fcodelineno-1-9)    raise ValueError("Invalid signature.")
`

If the signatures don't match, reject the request — return a `4xx` and don't process the payload.

June 8, 2026 

 Back to top 