This skill is for everything notifications. It is the only place where the HMAC validation pattern lives — every other skill defers here.
Step 0 — Connect only for an MCP-backed action
Do not inspect MCP state when this skill starts. Scaffolding and reviewing the receiver pattern are static operations and require no MCP connection.
Connect only immediately before one of these selected operations:
| Operation | Required MCP tool |
|---|---|
| Fetch a language-specific signature example | search_documentation |
| Resolve an unknown or country-specific topic | search_documentation |
| Register or rotate the callback URL | save_webhook |
| Confirm or diagnose webhook delivery | notifications_history |
On-demand authentication procedure:
- Attempt the intended tool directly if callable. Do not call
application_listas a connection probe. - If it is unavailable or returns an authentication error, call
mcp__plugin_mercadopago_mcp__authenticate. - Show the clickable OAuth link in the developer's language and instruct them to Cmd+Click (Mac) or Ctrl+Click (Windows/Linux), without copying the URL into an external browser.
- When the developer returns, retry the intended tool directly. Never ask them to paste a callback URL.
- If MCP tools are not loaded, explain how to enable
plugin:mercadopago:mcp. Keep any static scaffold/review work available offline.
Show this prerequisites checklist only when the developer selects an MCP-backed action:
Before configuring webhooks live, you'll need:
- [ ] A Mercado Pago developer account
- [ ] An app created in the Developer Dashboard
- [ ] Test credentials: APP_USR- access token + public key (tab {test_tab})
- [ ] The webhook signature secret (Dashboard → Webhooks → Signature secret)
Connection will be requested immediately before the selected MCP operation.
Step 1 — Decide the action
Ask the developer (or infer from $ARGUMENTS) which of these they want:
| Action | Tool to call | When |
|---|---|---|
| Configure the webhook URL on the MP application | save_webhook |
First time setup or rotating the endpoint |
| Diagnose delivery failures | notifications_history |
Investigating missed/failed notifications |
| Scaffold the receiver code | (no MCP call — render the pattern below) | Adding the receiver to the codebase |
You may chain them: scaffold the receiver → save_webhook → trigger a real test payment → use notifications_history to confirm delivery.
Step 2 — Receiver pattern (HMAC-SHA256)
Mercado Pago signs every notification with the secret returned in the dashboard at Webhooks → Signature secret. The x-signature header is composed of ts=...,v1=... where v1 is the HMAC-SHA256 of the canonical string "id:{data.id};request-id:{x-request-id};ts:{ts};".
Every receiver MUST:
- Read
x-signatureandx-request-idfrom the request headers. - Parse
tsandv1out ofx-signature. - Build the canonical string with
data.id(from the JSON body) andx-request-idandts. - Compute
HMAC-SHA256(canonical, secret)and compare in constant time withv1. - Respond
200immediately if the signature is valid — process the event asynchronously afterwards. Mercado Pago retries on non-200 responses with exponential backoff for up to ~24 hours. - Be idempotent: the same notification id may arrive more than once. Use
data.id+ topic as the dedup key.
Canonical string
id:<data.id>;request-id:<x-request-id>;ts:<ts>;
Reference snippet (Node.js, Express)
import crypto from "node:crypto";
const SECRET = process.env.MP_WEBHOOK_SECRET;
export function mpWebhook(req, res) {
const signature = req.header("x-signature") ?? "";
const requestId = req.header("x-request-id") ?? "";
const parts = Object.fromEntries(
signature.split(",").map((p) => p.split("=").map((s) => s.trim()))
);
const ts = parts.ts;
const v1 = parts.v1;
const dataId = req.body?.data?.id;
if (!ts || !v1 || !dataId || !requestId) return res.status(400).end();
const canonical = `id:${dataId};request-id:${requestId};ts:${ts};`;
const expected = crypto.createHmac("sha256", SECRET).update(canonical).digest("hex");
const ok = expected.length === v1.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
if (!ok) return res.status(401).end();
res.status(200).end();
// process asynchronously after responding 200
queueMicrotask(() => handleEvent(req.body));
}
For other languages, apply Step 0 immediately before querying MCP search_documentation with:
"webhook signature validation {language}"(e.g., python, java, php, ruby, go, dotnet).
Step 3 — Topics
The notification body contains type (the topic) and data.id. Common topics:
| Topic | When | Resource to fetch |
|---|---|---|
payment |
Payment status change (Payments API) | GET /v1/payments/{id} |
orders |
Point / QR Code event (Orders API) | GET /v1/orders/{id} |
merchant_order |
Merchant order updated — legacy (Checkout Pro / QR attended via legacy API) | GET /merchant_orders/{id} |
topic_claims_integration_wh |
Chargebacks | GET /v1/chargebacks/{id} |
point_integration_wh |
Point device events — legacy (old Point Integration API) | Point legacy API — query MCP for the country |
subscription_preapproval |
Subscription status change | GET /preapproval/{id} |
subscription_authorized_payment |
Recurring charge attempt | GET /authorized_payments/{id} |
If a topic is not in this table, apply Step 0 immediately before querying MCP for the latest list rather than guessing.
Step 4 — Configure on Mercado Pago (save_webhook)
Connect on demand: apply Step 0 immediately before
save_webhook. Do not callapplication_listfirst.
mcp__plugin_mercadopago_mcp__save_webhook(
callback="https://<production-url>/mp/webhook",
callback_url="https://<your-domain>/mp/webhook",
topics=["payment", "merchant_order", ...]
)
Confirm the response shows the URL and topics correctly registered.
Step 5 — Smoke test (real test payment)
simulate_webhook no longer exists in the MCP. To test your receiver:
- Make a real payment using test credentials + test user + test card
- The webhook fires automatically when the payment status changes
- Verify your receiver returned
200and processed the event idempotently
Apply Step 0 immediately before using notifications_history to confirm delivery:
mcp__plugin_mercadopago_mcp__notifications_history()
Step 6 — Diagnose missed deliveries
Connect on demand: apply Step 0 immediately before
notifications_history. Do not callapplication_listfirst.
mcp__plugin_mercadopago_mcp__notifications_history()
Returns delivery metrics and a breakdown of failures (timeouts, non-200 responses, signature mismatches). Use this when notifications are missing in production.
Gotchas
- Respond
200before processing. A long synchronous handler causes retries that flood the receiver and can mask the real failure. - Mercado Pago retries on non-200 with exponential backoff up to ~24h — a transient bug becomes a flood of duplicates.
- Make handlers idempotent. Use
data.id+typeas the dedup key. - Never trust the JSON body alone — always validate the signature first.
- IPN (the legacy
?id=&topic=GET-style notification) is deprecated. New integrations use only the modern signed webhook described here.
What this skill does NOT do
- It does not scaffold the surrounding integration. Use
mp-integrate. - It does not evaluate quality. Use
mp-review. - It does not invent topic names from memory — query MCP if unsure.