> ## Documentation Index
> Fetch the complete documentation index at: https://docs.deck.co/llms.txt
> Use this file to discover all available pages before exploring further.

# HMAC

## HMAC Signature

HMAC (Hash-based Message Authentication Code) is a method used to **authenticate a message** using a **shared secret and a hash function**. It allows our clients to verify that the received webhook is from **a trusted source** (Deck) and that the information **wasn't tampered with**, in which case the signature won't match the shared secret. We use SHA-256, so trying to verify the hash with anything else will not work.

## Why HMAC matters

HMAC adds an extra layer of protection to your webhooks by making sure that:

* **You can detect tampering** – If the message is altered in transit, its signature won’t match.

* **Only trusted senders can sign messages** – A valid signature can only be generated with the shared secret key.

* **Replay attacks are blocked** – Including a timestamp or nonce prevents attackers from resending old messages.

* **Collisions are highly unlikely** – Strong cryptographic hash functions make it extremely rare for two different messages to produce the same signature.

## Where it is

An HMAC key is generated automatically when creating an account. To access it, go to your Dashboard and click on "API Keys" from the left menu. Click on "Security" and you will see your HMAC Key on top.

The HMAC Key is the same for both Live and Sandbox environments.

## How it will look in a Webhook

Any Webhook that will be sent to you will include an `X-Signature` header containing the HMAC hash of the payload. This signature can be validated using the aforementioned HMAC key.

## Type of HMAC

The HMAC is in **Base64**.

Note that Base64 is used to encode the raw binary output of the HMAC hash into a text-safe string so it can be sent via HTTP headers or stored easily. HMAC is not used to encrypt the rest of the information passed through the webhook, but its presence does not affect the [encryption](/guides/others/encryption) already in place.

## Code to verify the HMAC

To verify that the HMAC received matches the one for your organization, you can use the following code examples.

<CodeGroup>
  ```javascript Javascript theme={null}
  const crypto = require("crypto");

  // Provided Base64-encoded secret
  const secretBase64 = <<YOUR_DECK_WEBHOOK_SECRET_FROM_DASHBOARD>>;

  // JSON body payload as a raw string (must match byte-for-byte) - This is AN EXAMPLE Webhook Event Body
  const body = `{"link_token":"link-production-77bd1897-b21b-4eb8-fca6-08dda362f7ed","public_token":"public-production-b7500d0c-920d-46cf-44eb-08dda3634c66","webhook_type":"Link","webhook_code":"ConnectionCreated","environment":"Production"}`;

  // Decode the Base64 secret into a raw byte buffer
  const key = Buffer.from(secretBase64, "base64");

  // Create the HMAC hash
  const hmac = crypto.createHmac("sha256", key);
  hmac.update(body);
  const computedSignature = hmac.digest("base64");

  // Output
  console.log("Expected Signature:", <<X_SIGNATURE_FROM_HEADER>>);
  console.log("Computed Signature:", computedSignature);
  ```

  ```python Python theme={null}
  import hmac
  import hashlib
  import base64

  # Provided Base64-encoded secret from your Deck dashboard.
  # IMPORTANT: Replace `<<YOUR_DECK_WEBHOOK_SECRET_FROM_DASHBOARD>>` with your actual secret.
  # This secret should be kept confidential.
  secret_base64 = "<<YOUR_DECK_WEBHOOK_SECRET_FROM_DASHBOARD>>"

  # JSON body payload as a raw string.
  # This must match the webhook event body exactly, byte-for-byte, as received.
  # This is an EXAMPLE Webhook Event Body.
  body = '{"link_token":"link-production-77bd1897-b21b-4eb8-fca6-08dda362f7ed","public_token":"public-production-b7500d0c-920d-46cf-44eb-08dda3634c66","webhook_type":"Link","webhook_code":"ConnectionCreated","environment":"Production"}'

  # --- Signature Calculation ---

  # Decode the Base64 secret into raw bytes.
  # In Python, the hmac module expects a bytes-like object for the key.
  key_bytes = base64.b64decode(secret_base64)

  # The message body must also be encoded to bytes.
  body_bytes = body.encode('utf-8')

  # Create the HMAC-SHA256 hash.
  # hashlib.sha256 is the hashing algorithm.
  # key_bytes is the secret key.
  hmac_sha256 = hmac.new(key_bytes, body_bytes, hashlib.sha256)

  # Compute the signature digest and encode it to Base64.
  # .digest() returns the raw byte hash.
  # base64.b64encode() encodes these bytes to Base64.
  # .decode('utf-8') converts the resulting bytes to a string for printing.
  computed_signature = base64.b64encode(hmac_sha256.digest()).decode('utf-8')

  # --- Output ---

  # Replace `<<X_SIGNATURE_FROM_HEADER>>` with the signature you receive in the X-Signature header
  # from the webhook request. You'll compare your computed signature against this.
  print("Expected Signature (from header):", "<<X_SIGNATURE_FROM_HEADER>>")
  print("Computed Signature:", computed_signature)

  # You would then compare `computed_signature` with the `X-Signature` header value
  # received in your webhook request to verify its authenticity.
  if computed_signature == "<<X_SIGNATURE_FROM_HEADER>>":
      print("\nSignature verification successful! The webhook is authentic.")
  else:
      print("\nSignature verification FAILED! The webhook may be tampered with or invalid.")
  ```

  ```go Go theme={null}
  package main

  import (
  	"crypto/hmac"
  	"crypto/sha256"
  	"encoding/base64"
  	"fmt"
  )

  func main() {
  	// Provided Base64-encoded secret from your Deck dashboard.
  	// IMPORTANT: Replace `<<YOUR_DECK_WEBHOOK_SECRET_FROM_DASHBOARD>>` with your actual secret.
  	// This secret should be kept confidential.
  	secretBase64 := "<<YOUR_DECK_WEBHOOK_SECRET_FROM_DASHBOARD>>"

  	// JSON body payload as a raw string.
  	// This must match the webhook event body exactly, byte-for-byte, as received.
  	// This is an EXAMPLE Webhook Event Body.
  	body := `{"link_token":"link-production-77bd1897-b21b-4eb8-fca6-08dda362f7ed","public_token":"public-production-b7500d0c-920d-46cf-44eb-08dda3634c66","webhook_type":"Link","webhook_code":"ConnectionCreated","environment":"Production"}`

  	// --- Signature Calculation ---

  	// Decode the Base64 secret into raw bytes.
  	keyBytes, err := base64.StdEncoding.DecodeString(secretBase64)
  	if err != nil {
  		fmt.Println("Error decoding secret:", err)
  		return
  	}

  	// The message body must also be encoded to bytes.
  	bodyBytes := []byte(body)

  	// Create the HMAC-SHA256 hash.
  	// hmac.New takes the hash function (sha256.New) and the key.
  	h := hmac.New(sha256.New, keyBytes)
  	h.Write(bodyBytes) // Write the body bytes to the hmac hasher

  	// Compute the signature digest and encode it to Base64.
  	// h.Sum(nil) returns the raw byte hash.
  	// base64.StdEncoding.EncodeToString encodes these bytes to Base64.
  	computedSignature := base64.StdEncoding.EncodeToString(h.Sum(nil))

  	// --- Output ---

  	// Replace `<<X_SIGNATURE_FROM_HEADER>>` with the signature you receive in the X-Signature header
  	// from the webhook request. You'll compare your computed signature against this.
  	expectedSignature := "<<X_SIGNATURE_FROM_HEADER>>"
  	fmt.Println("Expected Signature (from header):", expectedSignature)
  	fmt.Println("Computed Signature:", computedSignature)

  	// You would then compare `computedSignature` with the `X-Signature` header value
  	// received in your webhook request to verify its authenticity.
  	if computedSignature == expectedSignature {
  		fmt.Println("\nSignature verification successful! The webhook is authentic.")
  	} else {
  		fmt.Println("\nSignature verification FAILED! The webhook may be tampered with or invalid.")
  	}
  }
  ```
</CodeGroup>

Make sure that the **Expected Signature** and **Computed Signature** match.
