Webhooks
Receive real-time notifications when payment and checkout events occur. DonutMe sends HTTPS POST requests with a signed JSON payload to your configured endpoint.
Setting Up Webhooks
- Navigate to Project Settings → Webhooks
- Enter your HTTPS endpoint URL and click Save
- Generate a signing secret via Generate / Rotate Secret
- (Optional) Filter which events you want to receive
Event Types
| Event | Description |
|---|---|
payment.confirmed | Payment confirmed on-chain |
payment.failed | Payment transaction failed |
checkout.session_created | New checkout session initiated |
checkout.session_completed | Checkout session completed with a confirmed payment |
checkout.session_expired | Checkout session expired without payment |
Webhook Payload
Every webhook delivery is an HTTPS POST with Content-Type: application/json.
{
"event": "payment.confirmed",
"timestamp": "2026-04-08T10:00:00.000Z",
"data": {
"transactionId": "uuid",
"projectId": "uuid",
"txHash": "0x...",
"quotedAmountUsd": "10.00",
"customerId": "uuid",
"customerEmail": "buyer@example.com",
"metadata": {}
}
}Payload fields vary by event type. All payloads include event, timestamp, and a data object.
Signature Verification
Every request includes an X-DonutMe-Signature header in the format:
v1,sha256=<hex-digest>Verify by computing HMAC-SHA256 of the raw request body using your signing secret:
class="text-pink-400">import crypto class="text-pink-400">from class="text-pink-400">class="text-lime-400">"crypto";
class="text-pink-400">function verifySignature(body, secret, signatureHeader) {
class="text-pink-400">const [, hash] = signatureHeader.split(class="text-pink-400">class="text-lime-400">"sha256=");
class="text-pink-400">const expected = crypto
.createHmac(class="text-pink-400">class="text-lime-400">"sha256", secret)
.update(body)
.digest(class="text-pink-400">class="text-lime-400">"hex");
class="text-pink-400">return crypto.timingSafeEqual(
class="text-sky-400">Buffer.class="text-pink-400">from(hash, class="text-pink-400">class="text-lime-400">"hex"),
class="text-sky-400">Buffer.class="text-pink-400">from(expected, class="text-pink-400">class="text-lime-400">"hex"),
);
}When you rotate your signing secret, both the old and new secrets are valid for 24 hours to allow a graceful migration.
Retry Policy
Failed deliveries (non-2xx responses or timeouts) are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 5 seconds |
| 2nd retry | 10 seconds |
| 3rd retry | 20 seconds |
| 4th retry | 40 seconds |
| 5th retry | 80 seconds |
After 5 failed attempts, the delivery is marked as permanently failed.
Circuit Breaker
If your endpoint accumulates 15 consecutive failures, webhook delivery is automatically disabled for that project. You can re-enable it from the Webhooks settings page once the issue is resolved.
Test Events
Use the Send Test Event button in the Webhooks settings to send a webhook.test event to your endpoint. This verifies connectivity without affecting real data.
Delivery Logs
View webhook delivery history in Project Settings → Webhooks → Delivery Logs. Each log entry shows:
- HTTP status code
- Response latency
- Event type
- Retry attempt number
- Error details (if any)