Webhooks
Webhooks let DPT notify your server in real time when events happen in your business. Instead of polling the API, you register an endpoint and DPT sends an HTTP POST to it whenever an event fires.
Setup
- Go to Dashboard → Webhooks
- Click Add Webhook
- Enter your endpoint URL and select which events to subscribe to
- After creation, copy the signing secret — it is shown once only
Event Types
| Event | Fires when |
|---|---|
checkout.paid | A checkout is successfully paid |
checkout.expired | A checkout reaches its expiry time unpaid |
checkout.cancelled | A checkout is cancelled via the API or dashboard |
invoice.paid | An invoice is fully paid |
invoice.sent | An invoice is activated and sent to the customer |
payroll.executed | A payroll run completes all disbursements |
payout.completed | A payout to an external account is confirmed |
payout.failed | A payout fails |
Payload
All webhook deliveries share the same envelope:
{
"event": "checkout.paid",
"data": {
"id": "uuid",
"title": "Order #1234",
"amount": 50000000,
"currency": "USDC",
"status": "paid",
...
}
}The data object contains the full resource at the time of the event.
Request Headers
| Header | Value |
|---|---|
Content-Type | application/json |
X-DPT-Event | Event type, e.g. checkout.paid |
X-DPT-Signature | sha256={hex_signature} |
Verifying the Signature
Every delivery is signed with HMAC-SHA256 using your webhook's signing secret. Always verify the signature before processing the event.
Algorithm
- Read the raw request body as a string (do not parse it first)
- Compute
HMAC-SHA256(secret, raw_body)and hex-encode the result - Compare with the value after
sha256=inX-DPT-Signature - If they match, the payload is authentic
Examples
Node.js
const crypto = require('crypto');
function verifySignature(secret, rawBody, signatureHeader) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
}
// Express handler
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-dpt-signature'];
if (!verifySignature(process.env.WEBHOOK_SECRET, req.body, sig)) {
return res.status(401).send('Invalid signature');
}
const { event, data } = JSON.parse(req.body);
// process event...
res.sendStatus(200);
});Python
import hmac, hashlib
def verify_signature(secret: str, raw_body: bytes, signature_header: str) -> bool:
expected = 'sha256=' + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)
# Flask handler
@app.route('/webhooks', methods=['POST'])
def webhook():
sig = request.headers.get('X-DPT-Signature', '')
if not verify_signature(os.environ['WEBHOOK_SECRET'], request.data, sig):
abort(401)
payload = request.get_json()
event = payload['event']
data = payload['data']
# process event...
return '', 200Go
func verifySignature(secret, rawBody, signatureHeader string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(rawBody))
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signatureHeader))
}Retry Policy
If your endpoint returns a non-2xx HTTP status (or times out after 10 seconds), DPT retries the delivery with exponential backoff:
| Attempt | Delay after previous failure |
|---|---|
| 1 | Immediate |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 5 hours |
After 5 failed attempts, the delivery is marked failed and no further retries occur. You can view the full delivery history, including HTTP status codes and response bodies, in Dashboard → Webhooks.
Delivery History
Each event delivery is logged with:
- Status:
pending,delivered,failed - Attempt number
- HTTP response status from your server
- Timestamp
Access the log from the dashboard by clicking on any webhook endpoint.
Best Practices
- Return 2xx quickly — do heavy processing asynchronously (queue the event and acknowledge immediately)
- Handle duplicates — use
data.idto deduplicate in case of retries - Check the event type first — ignore events your code doesn't handle
- Use HTTPS — only register endpoints with valid TLS certificates
- Rotate secrets periodically — delete and recreate the webhook to get a new secret
