Introduction

This guide will walk you through setting up and securely handling webhooks. Our implementation follows the Standard Webhooks specification.

Getting Started

  1. Navigate to the DodoPayments Dashboard and go to Settings > Webhooks.
  2. Click on Add Webhook to create a new webhook endpoint.
  3. Enter the URL where you want to receive webhook events.
  4. Obtain your webhook Secret Key from the settings page. You’ll use this to verify the authenticity of received webhooks.

Webhook Delivery

Timeouts

  • Webhooks have a 10-second timeout window, including both connection and read timeouts.
  • If a webhook delivery attempt fails, we will retry sending the event using exponential backoff to avoid overloading your system.

Retries

  • We will attempt a maximum of 10 retries for each failed webhook delivery.
  • The initial retry delay is 6 seconds, and the maximum delay between retries is capped at 6 hours.

Idempotency

  • Each webhook event contains a unique webhook-id header. Use this to implement idempotency and avoid processing the same event multiple times.
  • Even if you receive the same event more than once (due to retries), your system should handle it gracefully without causing errors or duplicate actions.

Ordering

  • Webhook delivery order is not guaranteed, as webhooks may be delivered out of order due to retries or network issues.
  • Ensure your system can handle events arriving out of order by using the webhook-id header to process events correctly.
Please note that you will receive the latest payload at the time of delivery, regardless of when the webhook event was emitted.

Securing Webhooks

To ensure the security of your webhooks, always validate the payloads and use HTTPS.

Verifying Signatures

Each webhook request includes a webhook-signature header — an HMAC SHA256 signature of the webhook payload and timestamp, signed with your secret key. To verify a webhook came from DodoPayments:

  1. Compute the HMAC SHA256 of this string using your webhook secret key obtained from the DodoPayments Dashboard

  2. Concatenate the webhook-id, webhook-timestamp, and stringified payload values from the webhook with periods (.)
    The respective payloads for outgoing webhooks can be found in the Webhook Payload.

  3. Compare the computed signature to the received webhook-signature header value. If they match, the webhook is authentic.

Since we follow the Standard Webhooks specification, you can use one of their libraries to verify the signature: https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries

Responding to Webhooks

  • Your webhook handler must return a 2xx status code to acknowledge receipt of the event.
  • Any other response will be treated as a failure, and the webhook will be retried.

Outgoing Webhook Payload Structure

The following section describes the structure of the outgoing webhook request sent to your endpoint. This helps you understand what to expect and how to parse the payload.

Endpoint

POST /your-webhook-url

Headers

webhook-id
string
required

Unique identifier for the webhook.

webhook-signature
string
required

Signature of the Webhook (HMAC SHA256).

webhook-timestamp
string
required

Unix timestamp when the webhook was sent.

Request Body

The request body is a JSON object with the following structure:

{
  "business_id": "string",
  "type": "payment.succeeded | payment.failed |...",
  "timestamp": "2024-01-01T12:00:00Z",
  "data": {
    "payload_type": "Payment | Subscription | Refund | Dispute | LicenseKey",
    // ... event-specific fields (see below)
  }
}

For details on each event type, see the Webhook Event Type

For detailed payloads for each event type, see the Webhook Payload Event Guide

Responses

200
status code

Webhook processed successfully. You must return a 2xx status code to acknowledge receipt.

400
status code

Invalid request. The payload or headers are malformed.

401
status code

Invalid webhook signature. The signature verification failed.

Example code

An Express.js code snippet on how you can listen and verify our webhooks.

// index.ts in express 
const { Webhook } = require("standardwebhooks");

const webhook = new Webhook('DODO_PAYMENTS_WEBHOOK_KEY');


// Add this endpoint in the Dodo Payments Website | Side Bar -> Developer -> Webhooks -> create
app.post('/webhook/dodo-payments', async (req: any, res: any) => {
try {
const body = req.body;

const webhookHeaders: WebhookUnbrandedRequiredHeaders = {
  "webhook-id": (req.headers["webhook-id"] || "") as string,
  "webhook-signature": (req.headers["webhook-signature"] || "") as string,
  "webhook-timestamp": (req.headers["webhook-timestamp"] || "") as string,
};

const raw = JSON.stringify(body);

const samePayloadOutput = await webhook.verify(raw, webhookHeaders);
console.log(samePayloadOutput == body); //should be true except the order of keys

// ... Rest of your code HERE ...
res.status(200).json({ received: true });
} catch (error) {
console.error('Error processing webhook:', error);
res.status(400).json({ error: 'Webhook handler failed' });
}
});

We look forward to helping you implement seamless real-time notifications with webhooks! If you have any questions, please don’t hesitate to reach out to our support team.