Skip to main content

Receiving Webhooks in Production

Everything here follows from two facts about how BlockWyre delivers: a delivery attempt times out after 30 seconds, and after 5 consecutive failures the target is disabled and stops receiving anything.

A handler that is merely slow eventually becomes a handler that receives nothing.

Acknowledge first, work afterwards

Verify the signature, answer 200, and do the real work outside the request. Anything you do before responding is spending your 30 seconds.

app.post("/webhook", async (req, res) => {
await verifySignature(req); // fast, and must happen first
res.status(200).send("OK"); // acknowledge
await queue.add("process-webhook", req.body);
});

Expect the same webhook twice

A retry can arrive after your handler already succeeded — a response lost on the way back looks identical to a handler that never ran. Deduplicate on webhookId, which is stable across retries of the same event.

async function processWebhook(payload) {
const { webhookId } = payload;

if (await db.webhooks.findOne({ webhookId })) return;

await handleWebhookEvent(payload);
await db.webhooks.create({ webhookId, processedAt: new Date() });
}

Record the id in the same transaction as the work it guards. Recording it afterwards leaves a window where a crash costs you the event.

Reject stale deliveries

A valid signature proves a payload came from BlockWyre, not that it arrived just now. X-BlockWyre-Timestamp carries the send time in Unix seconds; refuse anything outside a window you choose.

const timestamp = parseInt(headers["x-blockwyre-timestamp"], 10);
const now = Math.floor(Date.now() / 1000);

if (Math.abs(now - timestamp) > 300) {
throw new Error("Webhook is outside the accepted time window");
}

Fail on the signature, not on your own bugs

These two failures deserve opposite answers:

What failedRespondWhy
Signature verification401The request is not ours. Retrying will not change that
Your own processing200Retrying replays a payload your code already could not handle, and five of those disable the target

Answer 200 and route the failure to your own alerting. Spending delivery attempts on a bug in your handler costs you unrelated events later.

app.post("/webhook", async (req, res) => {
try {
await verifySignature(req);
} catch {
return res.status(401).send("Unauthorized");
}

res.status(200).send("OK");

try {
await processWebhook(req.body);
} catch (error) {
logger.error("webhook processing failed", { error, body: req.body });
}
});

Watch the target, not just the handler

Your logs show what arrived. They cannot show what stopped arriving. Poll GET /api/v2/webhooks/targets and alert on disabledAt appearing — that field is the difference between a quiet day and a silently disabled integration.

Worth tracking as well: delivery success rate, handler processing time, and X-BlockWyre-Retry-Count. A retry count climbing above zero is the warning before the disable.

Testing locally

Targets must be reachable from the internet, so expose your local server with a tunnel:

ngrok http 3000

Register the tunnel URL as a target, and remember to delete it afterwards — an expired tunnel is a failing target, and five failures disable it.

Verify your signature code against the public JWKS endpoint before going near production. See Validating Webhooks.

Troubleshooting

SymptomCause
Deliveries stopped entirelyThe target was disabled after 5 failures. Check disabledAt, fix the endpoint, then re-enable it — see Managing Webhook Targets
Signature verification failsThe signature is over the raw body. A framework that parses and re-serializes JSON changes the bytes and invalidates it. Capture the raw body before any parsing middleware
Some events never arriveEvery target receives every event; there is no subscription. If an event is missing, it was not emitted — reconcile against the API
X-BlockWyre-Retry-Count keeps climbingYour endpoint is answering outside 2xx/3xx, or taking longer than 30 seconds
Nothing arrives at a new targetConfirm the URL is reachable from the public internet and presents a valid certificate

Support and Resources

If you need assistance or have any questions, our support team is here to help. You can contact our support team at support@blockwyre.com.

Stay Updated

Stay up-to-date with the latest news, updates, and features from BlockWyre by following us on social media:

We are excited to have you on board and look forward to seeing how you leverage BlockWyre's powerful tools to enhance your financial operations. Happy integrating!