CommerceHook

Square webhooks: the application model, the signature, and how to see them

Square's webhooks are well designed, and two things about them catch most developers out the first time: they belong to your application rather than a store, and they are signed over the URL as well as the body. Understand those two and the rest follows.

Subscriptions belong to the application

You do not point a webhook at a store. You create a subscription on your Square application (POST /v2/webhooks/subscriptions) with a list of event types and one notification URL, and from then on every merchant who has authorised the application delivers to that same URL. Each event names its seller in merchant_id and its subscription in square-subscription-id, and routing by those two is the work an integration does on every delivery. There is no challenge or confirmation step, so a subscription is live the moment it is created.

Event types are dot-notation names spanning the whole platform, from payment.updated and order.created to inventory.count.updated, invoice.payment_made and booking.updated. One subscription carries as many as you like, and the full catalogue is at GET /v2/webhooks/event-types.

The signature covers the URL, not just the body

This is the one that trips people up. Square computes the x-square-hmacsha256-signature as a base64 HMAC-SHA256 over the notification URL concatenated with the raw body, keyed with the signing key Square returned when the subscription was created. Reach for the body-only verification you use elsewhere and every delivery reads as tampered. The key is per subscription, so a delivery's square-subscription-id is how you pick the right one, and the comparison should be constant-time.

Retries run for a day, and nothing gets disabled

Square expects a prompt 2xx. When it does not get one, it retries on an exponential backoff for up to 24 hours before giving up on that delivery, and it never disables the subscription for repeated failures. A handler that struggles for an afternoon costs you only the deliveries that exhaust their day, not the subscription itself. A destination that always answers and captures each delivery removes even that.

The headers that matter

Debugging questions

Why are my Square webhooks not arriving?
Square does not disable a subscription after failures, so a dead subscription is rarely the cause. Check that the subscription is enabled and its notification URL is HTTPS and publicly reachable, and that you are looking at the right environment: sandbox events never appear against a production subscription, or the reverse. Square retries a failed delivery for up to 24 hours, so a handler that was down briefly will still receive the backlog.
How do I verify a Square signature?
Each delivery carries x-square-hmacsha256-signature: a base64 HMAC-SHA256 computed over the notification URL concatenated with the raw request body, keyed with the subscription signing key Square returns when the subscription is created. Compute the same HMAC and compare in constant time. Note the key is per subscription, and the value signed includes the URL, not just the body.
What is square-subscription-id for?
Every delivery names the subscription it came from in the square-subscription-id header. Since the signing key is per subscription, that header is how you find the right key to verify with, and how you tell deliveries from two subscriptions apart when they share an endpoint.
Do sandbox and production differ?
They are separate accounts with separate access tokens and separate API hosts (connect.squareupsandbox.com and connect.squareup.com). A subscription is created against one of them, and each delivery announces which in the square-environment header, so a captured event is never ambiguous about where it came from.

Seeing the deliveries

CommerceHook gives a Square application a stable HTTPS destination and shows every delivery in an inspector: the event badge, every header including the signature, and the payload as a JSON tree or raw. Because Square returns the signing key at creation, deliveries you register through us verify automatically. Registration happens from the dashboard, deliveries can be replayed at any handler, and commercehook listen streams them into the one on your laptop. Free for one endpoint, no card.