Free tool · Mercado Pago

Validate a Mercado Pago webhook
signature (x-signature)

Paste what Mercado Pago sent you and see whether the signature matches, which manifest was signed, and the expected hash next to the received one. Built for the moment a webhook handler keeps answering 401 and you cannot tell why.

Everything runs in your browser. The signature is computed on this page with the Web Crypto API. Nothing you type is sent to any server, and the secret never leaves this page.

The whole value, in the form ts=…,v1=…

Leave empty if the notification had none.

From the query string of the notification URL (?data.id=…). Leave empty if absent.

The secret signature of your application, from the Webhooks settings in the Mercado Pago developer panel.

How Mercado Pago signs a webhook

Every webhook notification carries an HMAC-SHA256 signature so you can prove it came from Mercado Pago and was not replayed. Verifying it takes five steps.

  1. 01

    Split the x-signature header

    The header looks like x-signature: ts=1704908010,v1=618c8534…. Split it on commas, then on the first equals sign of each part. ts is the signing timestamp (the official example is 10 digits, which is seconds; accept seconds or milliseconds) and v1 is the hash.

  2. 02

    Collect the three inputs

    data.id comes from the query string of the notification URL (?data.id=123456), x-request-id is a request header, and ts comes from step one.

  3. 03

    Build the manifest

    Concatenate id:<data.id>;request-id:<x-request-id>;ts:<ts>; exactly like that, with the trailing semicolon. If data.id or x-request-id is missing, leave out that whole segment. The Go SDK lowercases data.id before using it.

  4. 04

    Compute the HMAC-SHA256

    Sign the manifest with your application webhook secret as the key and take the lowercase hex digest.

  5. 05

    Compare, then check the timestamp

    Compare your digest to v1 with a constant-time comparison, then reject the request if ts is too old. A five-minute window is a common choice.

The same check in Node.js

A dependency-free implementation of the five steps, using node:crypto. Drop it in an Express or Next.js route and call it with the request headers, the data.id query parameter and your secret.

verify-mercadopago-webhook.ts
 1  import { createHmac, timingSafeEqual } from 'node:crypto';
 2  
 3  export function verifyMercadoPagoWebhook(req, secret: string): boolean {
 4    const xSignature = req.header('x-signature') ?? '';
 5    const xRequestId = req.header('x-request-id') ?? '';
 6    const dataId = String(req.query['data.id'] ?? '').toLowerCase();
 7  
 8    const parts = Object.fromEntries(
 9      xSignature.split(',').map((p) => p.trim().split('=')),
10    );
11    const { ts, v1 } = parts;
12    if (!ts || !v1) return false;
13  
14    let manifest = '';
15    if (dataId) manifest += `id:${dataId};`;
16    if (xRequestId) manifest += `request-id:${xRequestId};`;
17    manifest += `ts:${ts};`;
18  
19    const expected = createHmac('sha256', secret).update(manifest).digest('hex');
20    const a = Buffer.from(expected);
21    const b = Buffer.from(v1);
22    if (a.length !== b.length || !timingSafeEqual(a, b)) return false;
23  
24    // Replay protection: reject a signed timestamp older than 5 minutes.
25    // Mercado Pago's example ts has 10 digits (seconds); accept seconds or milliseconds.
26    const tsMs = Number(ts) < 1e12 ? Number(ts) * 1000 : Number(ts);
27    return Math.abs(Date.now() - tsMs) <= 5 * 60 * 1000;
28  }

Why is my Mercado Pago signature invalid?

Why does my Mercado Pago webhook signature not match?+
Almost always one of four things: the manifest is built differently from what Mercado Pago signed (wrong order, missing trailing semicolon, a segment included when it should be omitted), the secret belongs to another application, data.id was read from the wrong place or has the wrong case, or a proxy altered the headers. This page shows the manifest it signed so you can diff it against yours.
Does data.id have to be lowercase?+
The Go SDK lowercases data.id before putting it in the manifest. Numeric payment ids are unaffected, so the difference only shows up with alphanumeric ids. This validator lowercases data.id for you; if your handler does not, notifications with alphanumeric ids may fail.
Where does data.id come from?+
From the data.id query-string parameter of the notification URL, which matches data.id in the JSON body. Use the same value in both places and do not reformat it.
Can I use the secret from a different application?+
No. The secret belongs to one application: you find it in Your integrations, in the application, under Webhooks. If you have several applications or environments, use the secret of the application that sent the notification.
My signature is valid here but my server rejects it. Why?+
The hash can be right while the timestamp is stale. Replay protection rejects a signed ts that is too far from the current time; the useDeploy server uses a five-minute window. When you paste an old notification this page shows its age in seconds and warns you.
Is it safe to paste my webhook secret here?+
The computation happens in your browser with the Web Crypto API and the page makes no network request with your input, which you can confirm in your browser network tab. If you would still rather not paste a production secret, use the secret of a test application or rotate the secret afterwards.
Which notifications carry the x-signature header?+
Webhooks notifications include the x-signature header with ts and v1. This validator needs both: if your notification has no x-signature header, there is nothing to validate.
useDeploy at a glance
PriceFounder $99 or Lifetime $199, one-time payment (no subscription)
LicenseFull source, commercial use, unlimited projects. Founder is a frozen snapshot; Lifetime includes every future release.
StackNext.js frontend, Express API, Bun runtime, Prisma, BetterAuth
AuthBetterAuth: sessions, 2FA, magic links, password reset
Multi-tenancy and RBACOrganizations with members, invitations, roles and permissions
PaymentsPolar: full billing (checkout, customer portal, webhooks, plan sync). MercadoPago: Checkout Pro one-time payments with signed webhooks; subscriptions are not supported yet. Stripe: adapter scaffold, not charging yet.
Guardrails for AI agentsESLint-enforced architecture layers, Claude Code hooks and an AGENTS.md
Refund14 days
Try it: free accountSign up free and explore the dashboard: organizations, roles, billing, audit log. You buy the license from inside. https://usedeploy.dev/register
DocsDocumentation in English, with Spanish and Portuguese translations of the main guides