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
| Header | Purpose |
|---|---|
X-Event-ID | Unique event delivery ID for idempotency. |
X-Event-Type | Event type, such as payment.detected or payment.confirmed. |
X-Signature | HMAC-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-IDvalues 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.