Build
Webhooks
Signed events for completed, pending and refunded payments, with retry schedule and signature verification examples.
6 min read
Events
- payment.completed — the invoice was paid and verified.
- payment.pending — a TrxID was submitted but not yet matched.
- payment.refunded — a completed payment was refunded in full or part.
Payload
{
"event": "payment.completed",
"full_name": "Nusrat Jahan",
"email": "nusrat@example.com",
"amount": "1250.00",
"fee": "0.00",
"charged_amount": "1250.00",
"invoice_id": "6d1c...",
"metadata": { "order_id": "ORD-1042" },
"payment_method": "bkash",
"sender_number": "01712345678",
"transaction_id": "TFDEMO1234",
"date": "2026-10-29 14:20:11",
"status": "COMPLETED"
}Signature
Every delivery includes an X-TakaFlow-Signature header (sha256=<hex>) — an HMAC-SHA256 of the raw request body using the endpoint secret — and an X-TakaFlow-Event header naming the event. Compare the two with a constant-time function and reject anything that does not match.
import crypto from "node:crypto";
export function verify(rawBody, header, secret) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(header)
);
}Retries
Failed deliveries are retried five times with exponential backoff (1 minute, 5 minutes, 30 minutes, then 2 hours). Any 2xx response counts as success. Redirects are not followed. Every attempt, status code and response body is stored in your dashboard.
Retries run in the background loop that wakes on dashboard/checkout traffic and on the /api/cron/tick schedule, so a self-hosted install should call that endpoint every minute. Deliveries carry X-TakaFlow-Delivery and X-TakaFlow-Attempt headers — use the delivery ID to make your handler idempotent.
Respond quickly
Acknowledge with 200 before doing heavy work. Queue the payload in your own system and process it asynchronously; anything slower than 10 seconds is treated as a failure and retried.