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.

For the full delivery model, including payload shape, retry timing, and failure cases, see Webhook Delivery in a Bitcoin Payment API.