---
title: "Receive Square Webhooks Locally | WebhookRelay"
meta:
  "og:description": "Receive Square webhooks locally and test them on localhost without deploying. Inspect the real payment.created payload, forward to your handler, and verify the HMAC signature."
  "og:title": "Receive Square Webhooks Locally"
  description: "Receive Square webhooks locally and test them on localhost without deploying. Inspect the real payment.created payload, forward to your handler, and verify the HMAC signature."
---

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

# **Receive Square Webhooks Locally**

Receive Square webhooks locally and test them on localhost without deploying. Inspect the real payment.created payload, forward to your handler, and verify the HMAC signature.

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

You are building a Square integration — recording a sale, fulfilling an order, reconciling a refund — and you need to see your handler react to a real `payment.created` or `order.updated` event. The problem is immediate: Square will only POST to a **public URL**, and your handler is running on `localhost:8080`. Square has no way to reach it.

The usual workarounds are painful. Deploying to a staging server for every code change is slow. Pasting payloads from the docs into curl gives you a _guess_ at the real request, not the real headers and body Square actually sends. What you want is to **receive Square webhooks locally** — real events, hitting your local handler, with a URL that does not change every time you restart.

This guide shows how to do exactly that.

## [Why receiving Square webhooks locally is tricky](#why-receiving-square-webhooks-locally-is-tricky)

A webhook is just an HTTP request that Square sends to a URL when something happens in a seller's account. Square 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 Square 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 Square 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 Square actually sends. Open the free [Webhook Bin](https://webhookrelay.com/blog/receive-square-webhooks-locally/webhook-bin/) — no signup — and you get an instant public URL.

1. Copy the Webhook Bin URL.
2. In the [Square Developer Dashboard](https://developer.squareup.com/), open your application and go to **Webhooks → Subscriptions → Add Subscription** (or **Add Endpoint**).
3. Paste the URL into **Notification URL**, choose the **API version**, and select the event types you care about — for example `payment.created`, `payment.updated`, `order.updated`, `refund.created`, or `invoice.payment_made`.
4. Save the subscription.

Now click **Send Test Event** and pick an event type. Square immediately fires a sample notification, and you will see it land in Webhook Bin right away. Inspect the captured request: the full JSON body (with its `merchant_id`, `type`, `event_id`, and nested `data.object`), and every header, including `x-square-hmacsha256-signature` and `square-environment`.

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-square-webhooks-locally/blog/how-to-test-webhooks/) and [What is a webhook](https://webhookrelay.com/blog/receive-square-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 `square`. The bucket gives you a stable public input endpoint.

Start forwarding to your local server:

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

Now update the subscription's **Notification URL** to your Webhook Relay endpoint (or just create it there from the start). Hit **Send Test Event** again — or trigger a real action in the sandbox — and watch it arrive on `localhost`.

## [Square-specific configuration and quirks](#square-specific-configuration-and-quirks)

A few Square details worth knowing:

- **Where to add it:** webhook subscriptions live in the **Square Developer Dashboard → your application → Webhooks → Subscriptions**. Each subscription has its own notification URL, event list, and signature key.
- **Sandbox vs production:** Square keeps **separate** credentials and subscriptions for the **Sandbox** and **Production** environments. Test against the sandbox first, then move the subscription to production — the signature key is different per environment, so update your handler's config accordingly. The `square-environment` header tells you which one sent the request.
- **Event types:** subscribe to exactly the events you handle (`payment.created`, `order.updated`, `refund.updated`, `customer.created`, and so on). Branch on the `type` field in the JSON body rather than parsing the path.
- **Send Test Event:** the dashboard's **Send Test Event** button delivers a representative payload for any subscribed event type without needing to actually take a payment — perfect for wiring up and debugging your handler.
- **Retries:** if your endpoint does not return a `2xx` quickly, Square retries with backoff. Returning `200` fast and processing asynchronously keeps deliveries clean.

## [Step 3: Verify the Square webhook signature](#step-3-verify-the-square-webhook-signature)

Square signs every notification. It computes an **HMAC-SHA256** over the **notification URL concatenated with the raw request body**, using your subscription's **signature key**, then base64-encodes the result and sends it in the **`x-square-hmacsha256-signature`** header.

That URL-plus-body detail trips people up: you must use the _exact_ notification URL configured on the subscription and the **raw**, unparsed request body (no re-serialized JSON, no added whitespace). Find the signature key under **Webhooks → Subscriptions → your endpoint → Show** (in the Signature Key box). Recompute the digest and compare in constant time before trusting the payload.

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

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

This is where local development gets fast:

- **Replay from Square** by hitting **Send Test Event** again, or by re-triggering an action in the sandbox.
- **Replay from Webhook Relay** — past requests are stored on your bucket, so you can resend a captured event without touching Square 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 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 Square configuration never needs to change.

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

1. Inspect the real payload in the free [Webhook Bin](https://webhookrelay.com/blog/receive-square-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 square http://localhost:8080/webhook`.
3. Point your Square subscription's Notification URL at the stable endpoint, click Send Test Event, and watch it hit `localhost`.

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