---
title: "Test Stripe Connect Webhooks Locally | WebhookRelay"
meta:
  "og:description": "Test Stripe Connect webhooks locally on localhost without deploying. Capture connected-account events, forward to your handler, and verify the Stripe-Signature."
  "og:title": "Test Stripe Connect Webhooks Locally"
  description: "Test Stripe Connect webhooks locally on localhost without deploying. Capture connected-account events, forward to your handler, and verify the Stripe-Signature."
---

![Stripes](https://webhookrelay.com/blog/receive-stripe-connect-webhooks-locally/images/stripes.svg)

# **Test Stripe Connect Webhooks Locally**

Test Stripe Connect webhooks locally on localhost without deploying. Capture connected-account events, forward to your handler, and verify the Stripe-Signature.

![Test Stripe Connect Webhooks Locally (Connected-Account Webhooks on localhost)](https://webhookrelay.com/blog/receive-stripe-connect-webhooks-locally/images/blog/heroes/localhost.jpg)

You are building a Stripe Connect platform — a marketplace or SaaS that onboards merchants as connected accounts — and you need to see your handler react to a real `payment_intent.succeeded` or `account.updated` event that happened **on one of your connected accounts**. The problem is immediate: Stripe will only POST to a **public URL**, and your handler is running on `localhost:8080`. Stripe has no way to reach it.

The usual workarounds are painful. Deploying to staging for every code change is slow. Copying a sample payload from the docs into curl gives you a _guess_ at the real request — and worse, it omits the one field that makes Connect different: the `account` field telling you which connected account the event belongs to. What you want is to **test Stripe Connect webhooks locally** — real connected-account events, hitting your local handler, with a URL that does not change every time you restart.

This guide focuses on the Connect-specific bits. For the base Stripe flow, see [Receiving Stripe webhooks on localhost](https://webhookrelay.com/blog/receive-stripe-connect-webhooks-locally/blog/receiving-stripe-webhooks-localhost/).

## [What makes Connect webhooks different](#what-makes-connect-webhooks-different)

A regular Stripe webhook endpoint listens to events on **your own account**. A **Connect** endpoint listens to events on the **connected accounts** your platform manages. Three things change:

- **Scope:** the endpoint is configured to listen to "events on Connected accounts," so it receives events triggered by resources that live inside your connected accounts.
- **The `account` field:** every connected-account event includes a top-level `account` field (for example `acct_1A2b3C...`) identifying which connected account the event came from. Your handler must read this to route the event to the right merchant.
- **A separate signing secret:** the Connect endpoint has its **own** signing secret, distinct from any account-level endpoint you already have. Using the wrong secret is the single most common reason signature verification fails.

You only need **one** Connect endpoint for all of your connected accounts — Stripe does not require a separate endpoint per merchant.

## [Why testing Connect webhooks locally is tricky](#why-testing-connect-webhooks-locally-is-tricky)

Stripe sits on the public internet; your dev machine usually does not. It is behind a router, a corporate firewall, or both, with no public IP and no inbound ports open. So you need something in the middle: a public endpoint Stripe can hit, that relays each request down to your laptop without you opening a single firewall port. That is what [Webhook Relay](https://my.webhookrelay.com/register) does — and unlike a random tunnel URL, the endpoint is **stable**, so you configure Stripe once and never touch it again.

## [Step 1: Inspect the real payload with Webhook Bin](#step-1-inspect-the-real-payload-with-webhook-bin)

Before you write any handler code, find out what a connected-account event actually looks like. Open the free [Webhook Bin](https://webhookrelay.com/blog/receive-stripe-connect-webhooks-locally/webhook-bin/) — no signup — and you get an instant public URL.

1. Copy the Webhook Bin URL.
2. In the Stripe Dashboard, go to **Developers → Webhooks → Add endpoint**.
3. Paste the URL into **Endpoint URL**, add a description, then choose **Listen to events on Connected accounts** (this is what makes it a Connect endpoint).
4. Select events — for example `payment_intent.succeeded`, `account.updated`, `payout.paid` — and add the endpoint.

Trigger an action on a connected account (in test mode, complete a payment on a connected account, or update an account) and inspect the captured request in Webhook Bin: the full JSON body, the query string, and every header. Confirm you can see the top-level **`account`** field and the **`Stripe-Signature`** header. That `account` field is your proof the endpoint is scoped to Connect, not your platform account.

For more on this approach, see [How to test webhooks](https://webhookrelay.com/blog/receive-stripe-connect-webhooks-locally/blog/how-to-test-webhooks/) and [What is a webhook](https://webhookrelay.com/blog/receive-stripe-connect-webhooks-locally/blog/what-is-webhook/).

## [Step 2: Forward the events to localhost with the relay agent](#step-2-forward-the-events-to-localhost-with-the-relay-agent)

Once you know the payload, route those same events into your local handler. [Sign up for Webhook Relay](https://my.webhookrelay.com/register), install the `relay` agent (CLI or Docker), and create a bucket — say `stripe-connect`. The bucket gives you a stable public input endpoint.

Start forwarding to your local server:

```
relay forward --bucket stripe-connect http://localhost:8080/webhook
```

The agent opens an **outbound** connection to Webhook Relay and streams every incoming request down to `http://localhost:8080/webhook`. Because the connection is outbound, there are **no firewall ports to open and no public IP needed** — this works from your laptop, behind a corporate proxy, or inside a Kubernetes cluster. Running in Docker? The same command works in the official `webhookrelay/webhookrelayd` image. Full details are in the [localhost forwarding docs](https://webhookrelay.com/blog/receive-stripe-connect-webhooks-locally/docs/webhooks/internal/localhost/).

Now set the Connect endpoint's URL in **Developers → Webhooks** to your Webhook Relay endpoint (or create it there from the start, still with **Listen to events on Connected accounts** selected). Trigger a connected-account event and watch it arrive on `localhost`.

## [Stripe Connect-specific configuration and quirks](#stripe-connect-specific-configuration-and-quirks)

A few Connect details worth knowing:

- **Pick the Connect scope, not "Your account":** when adding the endpoint you must explicitly choose **Listen to events on Connected accounts**. An account-scoped endpoint will never see your connected accounts' events.
- **One endpoint covers all merchants:** the same Connect endpoint receives events for every connected account, distinguished by the `account` field. Route on that field in your handler.
- **Grab the Connect endpoint's signing secret:** open the endpoint in the Dashboard and reveal its **signing secret** (it starts with `whsec_`). This is _different_ from your account endpoint's secret — store it separately.
- **Test mode vs live mode:** webhook endpoints and their signing secrets are per-mode. Set up the endpoint in test mode while developing, then add the live endpoint and its own secret before going live.
- **Replay from Stripe:** the Dashboard lets you **resend** any past delivery to your endpoint with the original payload and signature — ideal for re-running a real connected-account event against your local handler.

## [Step 3: Verify the Stripe Connect webhook signature](#step-3-verify-the-stripe-connect-webhook-signature)

Stripe signs every request so you can confirm it really came from Stripe. It sends the digest in the **`Stripe-Signature`** header — an **HMAC-SHA256** computed over the string `timestamp.payload` using the endpoint's signing secret. For a Connect endpoint, that means the **Connect endpoint's** signing secret, not your account endpoint's.

Your handler should recompute the HMAC over the **raw** body (the exact bytes received, before any JSON parser reshapes them), check the timestamp is recent, and compare in constant time before trusting the payload. The Stripe SDKs do this for you via `Webhook.constructEvent` / `Webhook.constructEvent` equivalents — just be sure you pass the **Connect** secret. Mixing up the account and Connect secrets is the classic cause of "signature verification failed."

To sanity-check the underlying HMAC, paste a captured body, your secret, and the received signature into the free [HMAC signature verifier](https://webhookrelay.com/blog/receive-stripe-connect-webhooks-locally/hmac-verification/). For language-specific code and the common pitfalls (reading the body after a JSON parser has consumed it, timing-safe comparison, the timestamp tolerance), read [Verify a webhook signature](https://webhookrelay.com/blog/receive-stripe-connect-webhooks-locally/blog/verify-webhook-signature/).

## [Replay and iterate](#replay-and-iterate)

This is where local development gets fast:

- **Replay from Stripe** via the endpoint's **resend** action to re-run a real connected-account delivery against your handler.
- **Replay from Webhook Relay** — past requests are stored on your bucket, so you can resend a captured event without touching Stripe at all.
- **Iterate on your handler** by editing code and replaying the same delivery until your routing on the `account` field behaves correctly. No deploys just to test a single code path.

Because the Webhook Relay endpoint is stable, you can stop and restart the agent, reboot your machine, or come back next week — the Stripe Connect configuration never needs to change.

## [Get started](#get-started)

1. Inspect a real connected-account payload in the free [Webhook Bin](https://webhookrelay.com/blog/receive-stripe-connect-webhooks-locally/webhook-bin/) — no signup needed.
2. [Create a Webhook Relay account](https://my.webhookrelay.com/register), install the agent, and run `relay forward --bucket stripe-connect http://localhost:8080/webhook`.
3. Add a Stripe endpoint with **Listen to events on Connected accounts**, point it at the stable URL, trigger an event, and watch it hit `localhost`.

You will be testing real Stripe Connect events against your local handler in a few minutes — no deploys, no open firewall ports, and a URL you configure exactly once.