Skip to main content

Verify Webhook Signatures

Webhook events should be verified before your application updates an order. Manatee signs webhook requests with HMAC-SHA256 so your backend can reject forged or modified payloads.

Each API key has its own webhook signing secret. Open the dashboard, go to Wallet & API-Key, select the network, and reveal or rotate the webhook signing secret for that API key. Store the secret only on your server.

Required headers

HeaderPurpose
X-Event-IDUnique event delivery ID for idempotency.
X-Event-TypeEvent type, such as payment.detected or payment.confirmed.
X-SignatureHMAC-SHA256 signature of the raw request body.

Verification rules

  • Verify the signature before marking an order as paid.
  • Use the webhook signing secret that belongs to the API key used to create the payment.
  • Use the raw request body, not a parsed JSON object.
  • Compare signatures with a timing-safe comparison.
  • Store processed X-Event-ID values so repeated deliveries cannot double-credit an order.

Endpoint reachability

Your webhook endpoint must accept server-to-server HTTP requests from Manatee. If the endpoint is protected by Cloudflare, a WAF, bot protection, captchas, browser challenges, or login redirects, bypass those protections for the webhook path and verify authenticity with the X-Signature HMAC header instead.

Scope any bypass narrowly to the webhook route, for example /webhooks/manatee, rather than disabling protection for your whole domain.

Node.js example

const crypto = require('crypto');

function verifySignature(rawBody, signatureHeader, secret) {
if (!signatureHeader?.startsWith('sha256=')) {
return false;
}

const actual = Buffer.from(signatureHeader.slice('sha256='.length), 'hex');
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest();

if (actual.length !== expected.length) {
return false;
}

return crypto.timingSafeEqual(actual, expected);
}

Processing recommendation

Return a 2xx response only after your application has accepted the event. If your endpoint fails or returns a non-2xx response, Manatee retries delivery according to the retry schedule documented in the Quickstart.