> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive real-time event notifications from Dodo Payments. Set up endpoints, verify signatures, handle retries, and test webhooks during development.

<Frame>
  <img src="https://mintcdn.com/dodopayments/mOQO5ej_lx0yH9p-/images/cover-images/Webhooks.webp?fit=max&auto=format&n=mOQO5ej_lx0yH9p-&q=85&s=477435b1004773582aa51236b5cc58e7" alt="Webhook Cover Image" style={{ maxHeight: '500px', width: 'auto' }} width="1200" height="630" data-path="images/cover-images/Webhooks.webp" />
</Frame>

Webhooks deliver real-time notifications when events occur in your Dodo Payments account. Use them to automate workflows, update your database, send notifications, and keep your systems in sync.

<Info>
  Dodo Payments webhooks follow the [Standard Webhooks](https://standardwebhooks.com/) specification for signature verification and payload structure.
</Info>

## Key Features

Webhooks provide real-time delivery with built-in security, automatic retries, and event filtering. All official SDKs include signature verification helpers, and the dashboard offers testing, monitoring, and replay tools.

## Getting Started

<Steps>
  <Step title="Go to Developer → Webhooks">
    In the Dodo Payments Dashboard, navigate to **Developer → Webhooks**.
  </Step>

  <Step title="Click Add Endpoint">
    Click **Add endpoint** to create a new webhook receiver.
  </Step>

  <Step title="Enter Your Endpoint URL">
    Provide the HTTPS URL where Dodo Payments will send webhook events, or select an integration connector (Slack, Discord, Zapier, Resend, etc.) to route events to a third-party service without writing code.
  </Step>

  <Step title="Select Events">
    Choose which events to receive. Events are organized by resource (payment, subscription, dispute, etc.). You can select individual events or an entire resource to receive all related events.
  </Step>

  <Step title="Save">
    Click **Create endpoint**. Your webhook signing secret appears on the endpoint's **Overview** tab.
  </Step>
</Steps>

<Warning>
  Keep your webhook secret secure. Never expose it in client-side code or version control.
</Warning>

To rotate your webhook secret, open the endpoint and click **Rotate secret** next to the secret on the **Overview** tab. The old secret remains valid for 24 hours after rotation.

## Integration Connectors

Route webhook events directly to third-party services using integration connectors, eliminating the need to build and maintain custom webhook handlers.

### How Connectors Work

A connector transforms Dodo Payments events into the format the destination expects. Which details you provide depends on the destination:

| Connector type | What you provide | Destinations |
| - | - | - |
| **Incoming webhook URL** | A webhook URL you create in the vendor's dashboard. No API key. | [Slack](/integrations/slack), [Discord](/integrations/discord), [Microsoft Teams](/integrations/microsoft-teams), [Zapier](/integrations/zapier), [Windmill](/integrations/windmill) |
| **Vendor API** | An API key. The destination URL comes from the connector template. | [Resend](/integrations/resend), [SendGrid](/integrations/sendgrid), [Segment](/integrations/segment), [Customer.io](/integrations/customer-io), [Close CRM](/integrations/close-crm), [Loops](/integrations/loops) |

The dashboard shows all connectors available to your business. See [External Integrations](/integrations/introduction) for what each destination can do with the events.

### Setting Up a Connector

When creating or editing an endpoint, select a connector and the side sheet shows setup instructions for that destination. Test the transformation before saving to confirm events are converted correctly.

<Tip>
  Use a connector to reach a supported destination without writing code. If you need custom logic, use a standard endpoint with a [transformation](#transformations) instead.
</Tip>

## Configuring Subscribed Events

Configure which events each webhook endpoint receives.

<Steps>
  <Step title="Navigate to Webhook Endpoints">
    Go to **Developer → Webhooks** and click on your endpoint.
  </Step>

  <Step title="Open Event Configuration">
    Click **Edit** to open the endpoint configuration side sheet.
  </Step>

  <Step title="Select Events">
    The event type selector displays all available webhook events organized in a searchable tree, grouped by resource (e.g., `payment`, `subscription`, `dispute`). Check the boxes next to the events you want to receive. You can select individual events, an entire resource, or mix and match.
  </Step>

  <Step title="Save Configuration">
    Click **Save** to apply your changes.
  </Step>
</Steps>

<Warning>
  If you deselect all events, your webhook endpoint receives every event type. Select only the events your application needs.
</Warning>

## Event Catalog

Go to **Developer → Webhooks** and open the **Event catalog** tab to see every event type Dodo Payments can send. Select an event to view its schema and sample payload.

<Card title="Webhook Events Guide" icon="list" href="/developer-resources/webhooks/intents/webhook-events-guide">
  Browse events as reference documentation, grouped by resource.
</Card>

## Webhook Delivery

### Timeouts

Webhooks have a **30-second timeout** for both connection and read operations. Process webhooks asynchronously by returning a `200` status code immediately, then handle the event in the background.

### Automatic Retries

Failed deliveries are retried with exponential backoff, up to 8 attempts total:

| Attempt | Delay |
| - | - |
| 1 | Immediately |
| 2 | 5 seconds |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 5 hours |
| 7 | 10 hours |
| 8 | 10 hours (final) |

Use the dashboard to manually replay failed messages or bulk recover messages from a specific time range.

### Idempotency

Each webhook includes a unique `webhook-id` header. Store this ID to detect and skip duplicate events, since retries may deliver the same event multiple times.

```javascript theme={null}
// Use a persistent store such as your database in production. An in-memory Set is lost on restart.
const processedWebhooks = new Set();

app.post('/webhook', async (req, res) => {
  const webhookId = req.headers['webhook-id'];

  if (processedWebhooks.has(webhookId)) {
    return res.status(200).json({ received: true });
  }

  // processWebhook is your handler. Record the ID only after it succeeds,
  // so a failed attempt is processed again on retry.
  await processWebhook(req.body);
  processedWebhooks.add(webhookId);
  res.status(200).json({ received: true });
});
```

<Warning>
  Always implement idempotency checks. Due to retries, you may receive the same event multiple times.
</Warning>

### Event Ordering

Events may arrive out of order due to retries or network conditions. Each webhook includes a `timestamp` field; use it to order events if your application requires it. You always receive the latest payload state at delivery time.

## Securing Webhooks

Always validate webhook payloads and use HTTPS.

### Verifying Signatures

Each webhook includes a `webhook-signature` header: an HMAC SHA256 signature of the payload and timestamp, signed with your secret key.

#### SDK Verification (Recommended)

All official SDKs include built-in helpers. Set `DODO_PAYMENTS_WEBHOOK_KEY` when initializing the client, then call `unwrap()` to verify and parse the payload. Two methods are available:

* `unwrap` — Verifies the signature with your webhook secret key, then parses the payload.
* `unsafe_unwrap` — Parses the payload without verifying it. Use it for testing only.

The method names follow each language's conventions: `unwrap` / `unsafeUnwrap` in TypeScript, `unwrap` / `unsafe_unwrap` in Python, and `Unwrap` / `UnsafeUnwrap` in Go.

<Tip>
  Provide your webhook secret via `DODO_PAYMENTS_WEBHOOK_KEY` when initializing the Dodo Payments client.
</Tip>

<CodeGroup>
  ```typescript TypeScript/Node.js theme={null}
  import DodoPayments from 'dodopayments';
  import express from 'express';

  const app = express();
  app.use(express.raw({ type: 'application/json' }));

  const client = new DodoPayments({
    bearerToken: process.env.DODO_PAYMENTS_API_KEY,
    webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
  });

  app.post('/webhook', async (req, res) => {
    try {
      const event = client.webhooks.unwrap(req.body.toString(), {
        headers: {
          'webhook-id': req.headers['webhook-id'] as string,
          'webhook-signature': req.headers['webhook-signature'] as string,
          'webhook-timestamp': req.headers['webhook-timestamp'] as string,
        },
      });
      res.json({ received: true });
    } catch (error) {
      res.status(401).json({ error: 'Invalid signature' });
    }
  });
  ```

  ```python Python theme={null}
  from fastapi import FastAPI, Request, HTTPException
  from dodopayments import DodoPayments
  import os

  app = FastAPI()
  client = DodoPayments(
      bearer_token=os.getenv("DODO_PAYMENTS_API_KEY"),
      webhook_key=os.getenv("DODO_PAYMENTS_WEBHOOK_KEY"),
  )

  @app.post("/webhook")
  async def handle_webhook(request: Request):
      try:
          event = client.webhooks.unwrap(
              await request.body(),
              headers={
                  "webhook-id": request.headers.get("webhook-id", ""),
                  "webhook-signature": request.headers.get("webhook-signature", ""),
                  "webhook-timestamp": request.headers.get("webhook-timestamp", ""),
              },
          )
          return {"received": True}
      except Exception:
          raise HTTPException(status_code=401, detail="Invalid signature")
  ```

  ```go Go theme={null}
  import (
  	"io"
  	"net/http"
  	"os"
  	"github.com/dodopayments/dodopayments-go"
  	"github.com/dodopayments/dodopayments-go/option"
  )

  func webhookHandler(w http.ResponseWriter, r *http.Request) {
  	client := dodopayments.NewClient(
  		option.WithBearerToken(os.Getenv("DODO_PAYMENTS_API_KEY")),
  		option.WithWebhookKey(os.Getenv("DODO_PAYMENTS_WEBHOOK_KEY")),
  	)
  	rawBody, _ := io.ReadAll(r.Body)
  	event, err := client.Webhooks.Unwrap(rawBody, r.Header)
  	if err != nil {
  		http.Error(w, "Invalid signature", http.StatusUnauthorized)
  		return
  	}
  	_ = event // Handle the verified event here.
  	w.WriteHeader(http.StatusOK)
  }
  ```
</CodeGroup>

#### Manual Verification (Alternative)

If you're not using an SDK, verify the signature yourself:

1. Build the signed content by joining `webhook-id`, `webhook-timestamp`, and the raw request body with periods: `{id}.{timestamp}.{body}`. Use the raw body exactly as received, before any JSON parsing.
2. Take your webhook secret. If it starts with `whsec_`, remove that prefix, then base64-decode the rest to get the signing key.
3. Compute the HMAC-SHA256 of the signed content with the signing key, and base64-encode the result.
4. The `webhook-signature` header holds one or more space-separated signatures, each in the form `v1,<base64-signature>`. The request is valid if any `v1` signature matches yours. Compare with a constant-time function.
5. Reject the request if `webhook-timestamp` is too far from the current time, to prevent replay attacks. The Standard Webhooks libraries allow 5 minutes.

See the [Standard Webhooks libraries](https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries) for reference implementations. For event payload formats, see the [Webhook Payload](/developer-resources/webhooks/intents/payment).

### Source IP Addresses

Signature verification is the supported authentication method. It proves the request was signed with your webhook secret, which a network-level check cannot do.

Webhook deliveries come from a pool of IP addresses that changes over time. Do not rely on IP allowlists for authentication. Always verify the `webhook-signature` header instead, as described in [Verifying Signatures](#verifying-signatures).

If your firewall requires an allowlist:

* **Do not hardcode addresses permanently.** Ranges change over time, and stale rules silently block deliveries.
* **Request the current ranges** from [support@dodopayments.com](mailto:support@dodopayments.com) before locking down a firewall.
* **Watch for change notices.** When delivery addresses change, we notify affected merchants by email — apply updates before the stated date.
* **Keep signature verification enabled** regardless of any network rules you add.

<Tip>
  On serverless and managed hosting platforms, inbound IP filtering is often unavailable or impractical. Signature verification is the correct control in those environments.
</Tip>

<Info>
  A blocked delivery is treated as a failure and is retried on the schedule described in [Automatic Retries](#automatic-retries). If firewall rules caused deliveries to fail, you can re-send them once the rules are fixed — see [Replaying and Recovering Messages](#replaying-and-recovering-messages).
</Info>

### Responding to Webhooks

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

### Best Practices

* **Use HTTPS only.** HTTP endpoints are vulnerable to interception.
* **Respond immediately.** Return a `200` status code right away, then process the event asynchronously.
* **Implement idempotency.** Use the `webhook-id` header to detect and skip duplicate events.
* **Secure your secret.** Store `DODO_PAYMENTS_WEBHOOK_KEY` in environment variables or a secrets manager, never in version control.

## Webhook Payload Structure

### Request Format

```http theme={null}
POST /your-webhook-url
Content-Type: application/json
```

### Headers

<ParamField header="webhook-id" type="string" required>
  Unique identifier for this webhook event. Use for idempotency checks.
</ParamField>

<ParamField header="webhook-signature" type="string" required>
  HMAC SHA256 signature for verifying webhook authenticity.
</ParamField>

<ParamField header="webhook-timestamp" type="string" required>
  Unix timestamp (in seconds) when the webhook was sent.
</ParamField>

### Request Body

<ResponseField name="business_id" type="string" required>
  Your Dodo Payments business identifier.
</ResponseField>

<ResponseField name="type" type="string" required>
  Event type that triggered this webhook (e.g., `payment.succeeded`, `subscription.active`).
</ResponseField>

<ResponseField name="timestamp" type="string" required>
  ISO 8601 formatted timestamp of when the event occurred.
</ResponseField>

<ResponseField name="data" type="object" required>
  Event-specific payload containing detailed information about the event.

  <Expandable title="Data object properties">
    <ResponseField name="payload_type" type="string">
      Type of resource. One of: `Payment`, `Subscription`, `Refund`, `Dispute`, `LicenseKey`, `CreditLedgerEntry`, `CreditBalanceLow`, `AbandonedCheckout`, `DunningAttempt`, `EntitlementGrant`, or `Payout`.
    </ResponseField>

    Additional fields vary by event type. See the event-specific documentation for complete schemas.
  </Expandable>
</ResponseField>

### Example Payload

```json theme={null}
{
  "business_id": "bus_H4ekzPSlcg",
  "type": "payment.succeeded",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "payload_type": "Payment",
    // ... event-specific fields
  }
}
```

<CardGroup cols={2}>
  <Card title="Event Types" icon="list" href="/developer-resources/webhooks/intents/webhook-events-guide">
    Browse all available webhook event types
  </Card>

  <Card title="Event Payloads" icon="code" href="/developer-resources/webhooks/intents/payment">
    View detailed payload schemas for each event
  </Card>

  <Card title="Handle Payment Failures" icon="screwdriver-wrench" href="/developer-resources/handle-payment-failures">
    React to `payment.failed` and recover declined payments
  </Card>
</CardGroup>

## Testing Webhooks

### Send an Example Event

Test your webhook integration directly from the dashboard:

<Steps>
  <Step title="Navigate to Webhooks">
    Go to **Developer → Webhooks** and click on your endpoint.
  </Step>

  <Step title="Open Testing Tab">
    Click the **Testing** tab.
  </Step>

  <Step title="Send Example">
    Select an event type and click **Send example**. The sample payload is delivered to your endpoint URL exactly like a real event, signed the same way.
  </Step>

  <Step title="Check Your Endpoint">
    Confirm the event arrived, that your signature verification passed, and that you returned a `2xx` status code.
  </Step>
</Steps>

<Note>
  Failed messages sent from the Testing tab are retried on the normal retry schedule, like any other webhook.
</Note>

### Implementation Example

Complete Express.js implementation with webhook verification and handling:

<CodeGroup>
  ```typescript Express.js Webhook Handler theme={null}
  import { Webhook } from "standardwebhooks";
  import express from "express";

  const app = express();

  const webhook = new Webhook(process.env.DODO_PAYMENTS_WEBHOOK_KEY);

  // Use express.raw() for the webhook route so the exact bytes Dodo Payments
  // signed are preserved. Parsing with express.json() first and then calling
  // JSON.stringify(req.body) can alter the payload (e.g. whitespace), which
  // breaks signature verification.
  app.post(
    '/webhook/dodo-payments',
    express.raw({ type: "application/json" }),
    async (req, res) => {
      try {
        // Extract webhook headers
        const webhookHeaders = {
          "webhook-id": req.headers["webhook-id"] as string,
          "webhook-signature": req.headers["webhook-signature"] as string,
          "webhook-timestamp": req.headers["webhook-timestamp"] as string,
        };

        // Verify the webhook signature against the raw, unmodified body
        const payload = req.body.toString("utf8");
        await webhook.verify(payload, webhookHeaders);

        // Parse the payload only after verification succeeds
        const data = JSON.parse(payload);

        // Acknowledge receipt immediately
        res.status(200).json({ received: true });

        // Process webhook asynchronously
        processWebhookAsync(data).catch(console.error);

      } catch (error) {
        console.error('Webhook verification failed:', error);
        res.status(401).json({ error: 'Invalid signature' });
      }
    }
  );

  // Register express.json() for other routes after the webhook route so it
  // doesn't consume the raw body needed for signature verification above.
  app.use(express.json());

  async function processWebhookAsync(data: any) {
    // Handle the webhook event based on type
    switch (data.type) {
      case 'payment.succeeded':
        await handlePaymentSucceeded(data);
        break;
      case 'subscription.active':
        await handleSubscriptionActive(data);
        break;
      // Add more event handlers...
    }
  }
  ```

  ```javascript Testing Webhooks Locally theme={null}
  const { Webhook } = require("standardwebhooks");
  const axios = require("axios");
  const crypto = require("crypto");

  // Generate test webhook
  const timestamp = new Date();
  const webhookId = crypto.randomUUID();
  const payload = {
    business_id: "bus_H4ekzPSlcg",
    type: "payment.succeeded",
    timestamp: timestamp.toISOString(),
    data: {
      payload_type: "Payment",
      payment_id: "pay_test456",
      total_amount: 2999
    }
  };

  // Sign the payload
  const webhookSecret = process.env.DODO_PAYMENTS_WEBHOOK_KEY;
  const webhookInstance = new Webhook(webhookSecret);
  const payloadString = JSON.stringify(payload);
  const signature = webhookInstance.sign(webhookId, timestamp, payloadString);

  // Send test webhook. CommonJS scripts don't support top-level await, so use .then().
  axios.post('http://localhost:3000/webhook/dodo-payments', payload, {
    headers: {
      "webhook-id": webhookId,
      "webhook-timestamp": Math.floor(timestamp.getTime() / 1000),
      "webhook-signature": signature,
      "Content-Type": "application/json"
    }
  }).then((response) => {
    console.log("Test webhook sent successfully:", response.data);
  });
  ```
</CodeGroup>

<Tip>
  Test your webhook handler thoroughly using the dashboard testing interface before processing production events. This helps identify and fix issues early.
</Tip>

## Testing Webhooks with the CLI

The [Dodo Payments CLI](/developer-resources/sdks/cli) has two commands for testing webhooks during local development.

### Listen for Live Webhooks Locally

Forward real webhook events from your test mode account to your local development server:

```bash theme={null}
dodo wh listen
```

The CLI opens a WebSocket connection and forwards every webhook event to your local endpoint (e.g., `http://localhost:3000/webhook`), preserving all headers for signature verification testing.

<Info>
  The listener only works with test mode API keys. Run `dodo login` and select Test Mode first.
</Info>

### Trigger Mock Webhook Events

Send mock webhook payloads to any endpoint without creating real transactions:

```bash theme={null}
dodo wh trigger
```

This interactive tool lets you pick an event type and sends a realistic mock payload to your endpoint. It loops so you can test multiple events in one session.

The trigger command covers the subscription, payment, refund, dispute, license key, payout, credit, abandoned checkout, dunning, and entitlement grant families. It does not send `subscription.past_due` or `subscription.unpaused`. See [Supported Webhook Events](/developer-resources/sdks/cli#supported-webhook-events) for the exact list.

<Warning>
  Mock webhook payloads from `dodo wh trigger` are not signed. Use the unverified parse method (`unsafeUnwrap` in TypeScript, `unsafe_unwrap` in Python, `UnsafeUnwrap` in Go) in your webhook handler during testing only.
</Warning>

<Card title="CLI Webhook Testing Docs" icon="terminal" href="/developer-resources/sdks/cli#webhooks">
  See the full CLI webhook testing documentation
</Card>

## Advanced Settings

The **Advanced** tab provides additional configuration options for fine-tuning your webhook endpoint behavior.

### Rate Limiting (Throttling)

Control the rate at which webhook events are delivered to your endpoint. By default, webhooks have no rate limit applied and events are delivered as soon as they occur.

<Steps>
  <Step title="Open Advanced Tab">
    From your endpoint details page, click the **Advanced** tab.
  </Step>

  <Step title="Configure Rate Limit">
    Expand the **Endpoint throttling** section.
  </Step>

  <Step title="Set Your Limit">
    Enter the maximum number of messages per second, then click **Save**. Deliveries beyond this rate are queued rather than dropped.
  </Step>
</Steps>

### Custom Headers

Add custom HTTP headers to all webhook requests sent to your endpoint. Useful for authentication, routing, or adding metadata.

<Steps>
  <Step title="Add Headers">
    In the **Custom headers** section, enter a header name and value.
  </Step>

  <Step title="Add Multiple Headers">
    Click **Add header** for each additional header, then click **Save**.
  </Step>
</Steps>

### Transformations

Transformations allow you to modify a webhook's payload and optionally redirect it to a different URL. Use transformations to:

* Modify the payload structure before processing
* Route webhooks to different endpoints based on content
* Add or remove fields from the payload
* Transform data formats

<Steps>
  <Step title="Enable Transformations">
    In the **Transformation** section, turn on **Enable transformation**.
  </Step>

  <Step title="Configure Transformation">
    Write your transformation rules in JavaScript in the code editor, then click **Save**. The code must return the webhook object from `handler()`.
  </Step>

  <Step title="Test Transformation">
    Use the transformation test interface to verify your transformation works correctly before going live.
  </Step>
</Steps>

<Warning>
  Transformations can impact webhook delivery performance. Test thoroughly and keep transformation logic simple and efficient.
</Warning>

## Monitoring Webhook Logs

The **Logs** tab provides visibility into your webhook delivery status.

<Steps>
  <Step title="Navigate to Logs Tab">
    Go to **Developer → Webhooks** and open the **Logs** tab.
  </Step>

  <Step title="Browse Delivery History">
    View a table of all webhook delivery attempts with columns for Event type, Message ID, Event ID, Sent at, Attempted at, Response code, and Duration.
  </Step>

  <Step title="Search and Filter">
    Use the search bar to find specific messages by ID or event type. Filter by status (Succeeded, Failed, Pending, etc.) to focus on the events you need to investigate.
  </Step>

  <Step title="View Message Details">
    Click on any message to open the message detail page, which shows:

    * The complete webhook payload
    * Every delivery attempt with response code and duration
    * Timestamp of each attempt
    * Any error messages from your endpoint

    Each attempt carries a **Replay** action to re-drive that one message without leaving the page.
  </Step>
</Steps>

## Activity Monitoring

Go to **Developer → Webhooks** and open the **Activity** tab to see delivery performance across your endpoints.

**Delivery activity** plots attempts over time, bucketed as **Attempts per 5 minutes**, **Attempts per hour**, or **Attempts per day** depending on the window. Each bar is split by outcome, and hovering a segment shows the status, the number of attempts, and its share of the total. On an endpoint, **Delivery stats (last 24h)** on the **Overview** tab summarizes the same information for the past day.

<Tip>
  The **Error rate (24h)** column on the **Endpoints** tab shows which endpoints need attention at a glance.
</Tip>

## Replaying and Recovering Messages

How you re-drive a message depends on how many you need:

* **One message** — open it from the **Logs** tab and use the **Replay** action on the attempt.
* **A range of messages** — open the endpoint, since the bulk modes act on a single endpoint at a time.

### Replaying in Bulk

Open the endpoint from **Developer → Webhooks**. Three modes are available, each acting on that endpoint alone:

| Mode | What it does | What you set |
| - | - | - |
| **Recover failed messages** | Replays every message to this endpoint that failed. | A starting point: **8 hours ago**, **Yesterday**, **3 days ago**, **Last week**, or **2 weeks ago** |
| **Replay missing messages** | Replays messages that were never dispatched to this endpoint, for example after you subscribe it to a new event type. | The same starting points |
| **Bulk replay messages** | Replays messages matching the filters you choose, including ones that already delivered successfully. | **Since** and **Until** bounds, defaulting to the last two weeks, plus optional event types, channel, or tag |

<Steps>
  <Step title="Open More Actions">
    On the endpoint, open **More actions** and pick one of the three modes above.
  </Step>

  <Step title="Set the Range">
    Fill in the range that mode asks for, as listed in the table.
  </Step>

  <Step title="Start the Run">
    Click **Recover** or **Replay**, depending on the mode you picked.
  </Step>
</Steps>

Every run appears under **Replay history** on the endpoint's **Overview** tab, with its mode, time range, status, and the number of messages resent.

## Email Alerts

The webhooks dashboard doesn't offer email alerts for failing deliveries. To monitor deliveries, go to **Developer → Webhooks** and check the **Logs** and **Activity** tabs.

## Deploy to Cloud Platforms

Platform-specific guides for deploying webhook handlers to popular cloud providers:

<CardGroup cols={2}>
  <Card title="Vercel" icon="triangle" href="/developer-resources/webhooks/examples/vercel-example">
    Deploy webhooks to Vercel with serverless functions
  </Card>

  <Card title="Cloudflare Workers" icon="cloud" href="/developer-resources/webhooks/examples/cloudflare-example">
    Run webhooks on Cloudflare's edge network
  </Card>

  <Card title="Supabase Edge Functions" icon="database" href="/developer-resources/webhooks/examples/supabase-example">
    Integrate webhooks with Supabase
  </Card>

  <Card title="Netlify Functions" icon="circle-nodes" href="/developer-resources/webhooks/examples/netlify-example">
    Deploy webhooks as Netlify serverless functions
  </Card>
</CardGroup>

## Related API Reference

<CardGroup cols={2}>
  <Card title="Create Webhook" icon="code" href="/api-reference/webhooks/create-webhook">
    Create and configure webhook endpoints programmatically
  </Card>

  <Card title="List Webhooks" icon="list" href="/api-reference/webhooks/list-webhooks">
    Retrieve and manage your webhook endpoints
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.