Skip to content

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

  1. Go to Dashboard → Webhooks
  2. Click Add Webhook
  3. Enter your endpoint URL and select which events to subscribe to
  4. After creation, copy the signing secret — it is shown once only

Event Types

EventFires when
checkout.paidA checkout is successfully paid
checkout.expiredA checkout reaches its expiry time unpaid
checkout.cancelledA checkout is cancelled via the API or dashboard
invoice.paidAn invoice is fully paid
invoice.sentAn invoice is activated and sent to the customer
payroll.executedA payroll run completes all disbursements
payout.completedA payout to an external account is confirmed
payout.failedA payout fails

Payload

All webhook deliveries share the same envelope:

json
{
  "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

HeaderValue
Content-Typeapplication/json
X-DPT-EventEvent type, e.g. checkout.paid
X-DPT-Signaturesha256={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

  1. Read the raw request body as a string (do not parse it first)
  2. Compute HMAC-SHA256(secret, raw_body) and hex-encode the result
  3. Compare with the value after sha256= in X-DPT-Signature
  4. If they match, the payload is authentic

Examples

Node.js

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

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 '', 200

Go

go
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:

AttemptDelay after previous failure
1Immediate
25 minutes
330 minutes
42 hours
55 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.id to 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

DPT Merchant API