---
title: "Test Plaid Webhooks Locally | WebhookRelay"
meta:
  "og:description": "Test Plaid webhooks locally and receive them on localhost without deploying. Inspect the real payload, forward to your handler, and verify the signature."
  "og:title": "Test Plaid Webhooks Locally"
  description: "Test Plaid webhooks locally and receive them on localhost without deploying. Inspect the real payload, forward to your handler, and verify the signature."
---

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

# **Test Plaid Webhooks Locally**

Test Plaid webhooks locally and receive them on localhost without deploying. Inspect the real payload, forward to your handler, and verify the signature.

![Test Plaid Webhooks Locally (Receive Plaid Webhooks on localhost)](https://webhookrelay.com/blog/receive-plaid-webhooks-locally/images/blog/heroes/localhost.jpg)

You are building a Plaid integration and you need to watch your handler react to a real event. The problem hits immediately: Plaid will only POST to a **public URL**, and your handler is running on `localhost:8080`. Plaid has no way to reach it.

The usual workarounds are slow. Deploying to a staging environment for every code change kills your iteration speed. Copying a sample payload out of the docs into curl gives you a _guess_ at the real request, not the exact headers and body Plaid actually sends. What you really want is to **test Plaid webhooks locally** — real events, hitting your local handler, on a URL that does not change every time you restart.

This guide shows how to do exactly that.

## [Why testing Plaid webhooks locally is tricky](#why-testing-plaid-webhooks-locally-is-tricky)

A webhook is just an HTTP request that Plaid sends to a URL when something changes. Plaid lives on the public internet; your dev machine usually does not. It sits 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 Plaid 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 Plaid 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 Plaid actually sends. Open the free [Webhook Bin](https://webhookrelay.com/blog/receive-plaid-webhooks-locally/webhook-bin/) — no signup — and you get an instant public URL.

1. Copy the Webhook Bin URL.
2. Set the webhook URL in the Plaid Dashboard under **Team Settings → Webhooks**, or pass `webhook` when you create an Item via the API.
3. Trigger a real event and inspect the captured request.

You will see the full body and every header. The body is JSON keyed by `webhook_type` (e.g. `TRANSACTIONS`) and `webhook_code` (e.g. `SYNC_UPDATES_AVAILABLE`), plus an `item_id` and product-specific fields. You react to the type/code, then call the Plaid API to fetch the actual data.

Now you know the exact shape of the data before writing a line of code. For more on this approach, see [How to test webhooks](https://webhookrelay.com/blog/receive-plaid-webhooks-locally/blog/how-to-test-webhooks/) and [What is a webhook](https://webhookrelay.com/blog/receive-plaid-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 `plaid`. The bucket gives you a stable public input endpoint.

Start forwarding to your local server:

```
relay forward --bucket plaid 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-plaid-webhooks-locally/docs/webhooks/internal/localhost/).

Now point the Plaid webhook at your Webhook Relay endpoint (or create it there from the start), trigger an event, and watch it arrive on `localhost`.

## [Plaid-specific configuration and quirks](#plaid-specific-configuration-and-quirks)

A few Plaid details worth knowing:

- **Where to set it:** the Plaid Dashboard (**Team Settings → Webhooks**) for a default URL, or per-Item by passing `webhook` at Item creation.
- **Events:** webhooks are grouped by `webhook_type` and `webhook_code` — e.g. `TRANSACTIONS` / `SYNC_UPDATES_AVAILABLE`. The webhook is a _notification_; you then call the API to pull the data.
- **Sandbox:** use the Plaid Sandbox and `/sandbox/item/fire_webhook` to trigger test webhooks into your bin while you build.
- **Verification:** JWT/ES256, not HMAC — see the verification note below.

## [Step 3: Verify the Plaid webhook signature](#step-3-verify-the-plaid-webhook-signature)

Plaid's verification is unusual: rather than a shared-secret HMAC, it sends a **JWT (JWS signed with ES256)** in the **`Plaid-Verification`** header. To verify, read the JWT header's `kid`, fetch the matching public key from Plaid's `/webhook_verification_key/get` endpoint (cache it; keys rotate), validate the JWT signature, check the `iat` is recent, and confirm a SHA-256 of the raw request body equals the JWT's `request_body_sha256` claim. Only then trust the payload.

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

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

This is where local development gets fast:

- **Replay from Webhook Relay** — past requests are stored on your bucket, so you can resend a captured event against your handler without touching Plaid at all.
- **Iterate on your handler** by editing code and replaying the same delivery until it behaves correctly. No commits, no pushes, no deploys just to test a single code path.
- **Keep `relay forward` running** while you work — events stream straight to `localhost` as you trigger them in Plaid.

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

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

1. Inspect the real payload in the free [Webhook Bin](https://webhookrelay.com/blog/receive-plaid-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 plaid http://localhost:8080/webhook`.
3. Point your Plaid webhook at the stable endpoint, trigger an event, and watch it hit `localhost`.

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