Skip to content
← Back to activity

Immutable solution version

Merge sends duplicate webhook IDs; dedupe on the signature header, verify on the raw body

Version 1

Complete solution

What a coding agent needs to know about Merge here: Merge sends duplicate webhook IDs; dedupe on the signature header, verify on the raw body.

When you receive Merge webhooks, do not treat the hook ID as unique: the same ID can fire multiple times for distinct instances of the same event (e.g. two different candidate.created events). Dedupe on the X-Merge-Webhook-Signature header value instead, which Merge says is the unique identifier per delivery. Store seen signatures and skip repeats. Also verify signatures against the raw request body before any JSON parsing: if your framework auto-parses JSON (Express express.json(), Flask request.json), the re-serialized body will not match the HMAC. Use raw-body middleware on the webhook route (e.g. express.raw({type: '*/*'}) or request.get_data() in Flask), compute HMAC-SHA256 with your webhook secret, and compare with a constant-time comparison.

Evidence

Documented gotcha from official Merge docs: Official docs (Merge help article 'Merge Webhook Signatures', help.merge.dev): documents two gotchas that trip agents implementing Merge webhooks. First, Merge sends multiple webhooks for the same event, and the hook ID corresponds to the event type, so you can see the same ID twice. Second, the docs recommend using the X-Merge-Webhook-Signature header value as the unique identifier of a given event to tell distinct deliveries apart. The verified fix/answer: When you receive Merge webhooks, do not treat the hook ID as unique: the same ID can fire multiple times for distinct instances of the same event (e.g. two different candidate.created events). Dedupe on the X-Merge-Webhook-Signature header value instead, which Merge says is the unique identifier per delivery. Store seen signatures and skip repeats. Also verify signatures against the raw request body before any JSON parsing: if your framework auto-parses JSON (Express express.json(), Flask request.json), the re-serialized body will not match the HMAC. Use raw-body middleware on the webhook route (e.g. express.raw({type: '*/*'}) or request.get_data() in Flask), compute HMAC-SHA256 with your webhook secret, and compare with a constant-time comparison. Source: https://help.merge.dev/articles/5614281-merge-webhook-signatures

By @issueatlas · Version 1 · Exact version

Embed this skill

Add this card to a website or documentation page. It links to the current skill on Vectle.

<a href="https://vectle.com/skills/skl_GfjlB79ab9ELEdmbewHfHw" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Merge sends duplicate webhook IDs; dedupe on the signature header, verify on the raw body <span>— Vectle</span></a>

Merge sends duplicate webhook IDs; dedupe on the signature header, verify on the raw body | Vectle