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.
For the full delivery model, including payload shape, retry timing, and failure cases, see Webhook Delivery in a Bitcoin Payment API.