> For the complete documentation index, see [llms.txt](https://docs.tryterra.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tryterra.co/help-center/help-topics/webhook/security-signatures-and-network-access/signature-verification-secret-mismatch.md).

# Why does my webhook signature verification fail?

Signature failures almost always mean the signing secret used for verification does not match the one Terra signed with, not a change on Terra's side.

Signature failures almost always mean the signing secret used for verification does not match the one Terra signed with, not a change on Terra's side.

Each environment and each webhook has its own secret, so using a production secret in staging (or vice versa) yields `no matching signature`. A common pattern: multiple `dev_id`s (production, staging, testing) each have their own secret but all point at the same destination URL, so a verifier holding one secret rejects events signed by the others.

To fix it:

{% stepper %}
{% step %}
**Find the correct secret in the dashboard**: open the three-dots menu on the [webhook](https://docs.tryterra.co/unified-api/integration-setup/setting-up-data-destinations/webhooks), then Edit.
{% endstep %}

{% step %}
Either give each `dev_id` its own webhook URL/destination, or disable destinations on unused environments.
{% endstep %}

{% step %}
Upgrade to the latest SDK, which deprecates the legacy `secret_key` flow.
{% endstep %}
{% endstepper %}

## Small payloads verify but large ones fail

**If small payloads verify and large ones fail, the secret is right and the body is being changed before your code hashes it.** Terra signs every delivery the same way whatever its size: an HMAC-SHA256 over the timestamp, a full stop and the raw body bytes, sent in the `terra-signature` header as `t=...,v1=...`. The body is not compressed.

Look for something on your side that handles large bodies differently, such as a framework that parses and re-serialises the JSON, a body-size or buffering setting, or a proxy that re-encodes the body. Verify against the untouched raw bytes. If the bodies are simply too large for your stack, switch the webhook to Ping delivery, which sends a small signed notification with a download link instead of the full payload.

## Is the timestamp renewed on retries?

**Yes.** Each delivery attempt, including retries, is signed with a fresh `t` value at the moment it is sent. Your freshness check only needs to allow for network transit and clock skew, not the retry schedule.
