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

# Debug Stripe webhooks with signature verification

> Capture Stripe webhook events, verify HMAC signatures against your signing secret, inspect payloads, and replay events to staging after a fix.

Stripe signs every webhook delivery with an HMAC-SHA256 signature in the `Stripe-Signature` header. Before you wire Stripe to your real backend, use the Webhook Debugger to confirm that your signing secret is correct, inspect the full payload structure, and replay individual events without waiting for Stripe to resend them.

## What you'll accomplish

* Configure the actor to verify `Stripe-Signature` automatically
* Point your Stripe dashboard webhook URL at the generated endpoint
* Inspect captured events and confirm `signatureValid: true`
* Replay a specific event to staging after fixing downstream code
* Set up alerts for signature failures

## Configure the actor for Stripe

Start the actor with the following input. Replace `whsec_replace_me` with your actual Stripe webhook signing secret from the Stripe dashboard.

```json theme={null}
{
  "urlCount": 1,
  "retentionHours": 72,
  "authKey": "stripe-debug-key",
  "enableJSONParsing": true,
  "maskSensitiveData": true,
  "redactBodyPaths": [
    "body.data.object.customer_email",
    "body.data.object.metadata.internal_note"
  ],
  "signatureVerificationSecret": "whsec_replace_me",
  "signatureVerification": {
    "provider": "stripe",
    "tolerance": 300
  },
  "defaultResponseCode": 200,
  "defaultResponseBody": "{\"received\":true}",
  "forwardUrl": "https://staging.example.com/webhooks/stripe",
  "forwardHeaders": true,
  "replayMaxRetries": 3,
  "replayTimeoutMs": 10000,
  "alerts": {
    "slack": {
      "webhookUrl": "https://hooks.slack.com/services/AAA/BBB/CCC"
    }
  },
  "alertOn": ["signature_invalid", "5xx"]
}
```

<Note>
  Set `forwardHeaders: true` if your downstream receiver also validates `Stripe-Signature`. The actor preserves the original header when forwarding.
</Note>

After the actor starts, call `/info` to retrieve the generated webhook URL and confirm the active webhook ID.

## Point Stripe at your generated endpoint

<Steps>
  <Step title="Open the Stripe dashboard">
    Go to **Developers → Webhooks** in the Stripe dashboard and click **Add endpoint**.
  </Step>

  <Step title="Paste the generated URL">
    Copy the `/webhook/:id` URL from `/info` and paste it into the **Endpoint URL** field.
  </Step>

  <Step title="Select events">
    Choose the event types you want to test, such as `payment_intent.succeeded` or `checkout.session.completed`.
  </Step>

  <Step title="Copy the signing secret">
    After saving the endpoint, click **Reveal** under **Signing secret** and copy the `whsec_...` value into your actor input.
  </Step>
</Steps>

<Warning>
  Stripe frequently updates their webhook IP ranges. If you set `allowedIps`, check the [Stripe IP documentation](https://docs.stripe.com/ips#webhook-notifications) to keep your list current. Signature verification alone is more stable than IP allowlisting.
</Warning>

## Inspect a captured event

After Stripe delivers an event, query `/logs` to see what was captured:

```bash theme={null}
curl "https://<run-id>.runs.apify.net/logs?webhookId=wh_abc123&limit=10"
```

A captured Stripe event looks like this:

```json theme={null}
{
  "id": "evt_8m2L5p9xR",
  "webhookId": "wh_abc123",
  "timestamp": "2026-01-30T12:00:00.000Z",
  "method": "POST",
  "statusCode": 200,
  "contentType": "application/json",
  "signatureValid": true,
  "signatureProvider": "stripe",
  "body": {
    "type": "payment_intent.succeeded",
    "data": {
      "object": {
        "amount": 9999,
        "currency": "usd"
      }
    }
  }
}
```

`signatureValid: true` confirms the actor verified the `Stripe-Signature` header against your signing secret.

To filter for a specific event type after JSON parsing is enabled:

```bash theme={null}
GET /logs?webhookId=wh_abc123&body.type=checkout.session.completed
```

To retrieve the full payload for an event that was offloaded due to size:

```bash theme={null}
GET /logs/evt_8m2L5p9xR/payload
```

## Replay a Stripe event after fixing a bug

Once you fix the downstream handler, replay the original captured event to staging without waiting for Stripe to resend it:

```bash theme={null}
curl -X POST \
  "https://<run-id>.runs.apify.net/replay/wh_abc123/evt_8m2L5p9xR?url=https%3A%2F%2Fstaging.example.com%2Fwebhooks%2Fstripe"
```

The replay endpoint sends the original method and payload, automatically hydrates any large body that was offloaded to storage, and strips masked or transport-managed headers before forwarding.

<Tip>
  Use `?__status=500` appended to the webhook URL to force a temporary failure path and observe Stripe retry behavior without changing your saved actor configuration.
</Tip>

## Set up alerts for signature failures

Add an `alerts` block to your actor input and set `alertOn` to include `signature_invalid`:

```json theme={null}
{
  "alerts": {
    "slack": {
      "webhookUrl": "https://hooks.slack.com/services/AAA/BBB/CCC"
    }
  },
  "alertOn": ["signature_invalid", "5xx"]
}
```

The actor posts a notification to the configured Slack channel whenever a delivery fails signature verification. You can query the same failures directly:

```bash theme={null}
GET /logs?webhookId=wh_abc123&signatureValid=false
```

## Common failure patterns

| Signal                 | What it usually means                                                                    | What to do                                                                                                             |
| :--------------------- | :--------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------- |
| `signatureValid=false` | Wrong signing secret, modified raw body, or stale timestamp tolerance                    | Verify the `whsec_...` secret and inspect the raw captured request before any downstream transformation                |
| 5xx sender response    | Your test profile is simulating a failure, or a custom script changed `event.statusCode` | Remove the forced status or replay a healthy event after the fix is deployed                                           |
| Forwarding failures    | The webhook was accepted but the downstream bridge failed later                          | Check the `alertOn` notifications for `error` or `5xx` events, or review `/system/metrics` for forwarding error counts |
