Square sends an x-square-hmacsha256-signature header. It's an HMAC-SHA256 built from three things: your subscription's signature key, the subscription's notification URL, and the raw request body. The signed string is the URL followed directly by the body, and the result is base64-encoded. Unlike most providers, Square includes the URL, and that's where most failures come from.
Fixed it? Stop debugging Square webhooks: the Self-Hosted Webhook Relay Kit ($19, one-time) runs on Cloudflare's free tier and verifies x-square-hmacsha256-signature for you and retries failed deliveries.
http://, an internal host or a different path. A trailing-slash difference also breaks it. Hard-code the configured URL or load it from config.x-square-signature (HMAC-SHA1). Verify x-square-hmacsha256-signature instead.import { WebhooksHelper } from 'square';
app.post('/square', express.raw({ type: 'application/json' }), async (req, res) => {
const ok = await WebhooksHelper.verifySignature({
requestBody: req.body.toString('utf8'),
signatureHeader: req.get('x-square-hmacsha256-signature') || '',
signatureKey: process.env.SQUARE_SIGNATURE_KEY,
notificationUrl: process.env.SQUARE_NOTIFICATION_URL, // exactly as configured
});
if (!ok) return res.sendStatus(403);
res.sendStatus(200); // then process; de-dupe on event_id
});
expected = base64.b64encode(hmac.new(key.encode(), (NOTIFICATION_URL + raw_body).encode(), hashlib.sha256).digest()).decode()
ok = hmac.compare_digest(expected, request.headers.get('x-square-hmacsha256-signature', ''))
The guide is Stripe-only. The kit is a self-hosted Cloudflare Worker relay that adds retries, replay and a dead-letter list in front of your endpoint. It verifies Square's x-square-hmacsha256-signature out of the box. Set SCHEME_<ID>=square and your notification URL. It works the same way for Stripe, Lemon Squeezy, Shopify, Paddle Billing, Twilio (status callbacks) and GitHub.
Square's docs say the notification URL must use HTTPS and must respond with a 2xx as soon as possible. Notifications can arrive more than once, so de-duplicate on the event_id in the body.
Free download: Webhook debugging cheat sheet.
Fighting webhooks on more than one platform? The Fix Your Stripe Webhooks guide ($5) covers raw-body and signature problems in depth, and the Self-Hosted Webhook Relay Kit ($19) adds retries, replay and a dead-letter list in front of any endpoint.