Square Integration
Receive and route Square webhooks for payments, orders, customers, invoices, and inventory events.
Setup
1. Create a Source in Hookbase
curl -X POST https://api.hookbase.app/api/sources \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Square Production",
"slug": "square",
"verificationConfig": {
"type": "hmac",
"secret": "your-square-signature-key",
"algorithm": "sha256",
"header": "x-square-hmacsha256-signature",
"encoding": "base64"
}
}'Save your webhook URL:
https://api.hookbase.app/ingest/{orgSlug}/square2. Configure Square Webhook
- Go to the Square Developer Dashboard
- Select your application
- Navigate to Webhooks in the left sidebar
- Click Add Subscription
- Enter your Hookbase webhook URL as the Notification URL
- Select the events you want to receive
- Click Save
- Copy the Signature key from the subscription details
- Update your Hookbase source with this signature key
3. Create Routes
# Create destination for your payment handler
curl -X POST https://api.hookbase.app/api/destinations \
-H "Authorization: Bearer whr_your_api_key" \
-d '{"name": "Payment Service", "url": "https://api.myapp.com/webhooks/square"}'
# Create destination for order processing
curl -X POST https://api.hookbase.app/api/destinations \
-H "Authorization: Bearer whr_your_api_key" \
-d '{"name": "Order Service", "url": "https://api.myapp.com/webhooks/square-orders"}'
# Create route for payment events
curl -X POST https://api.hookbase.app/api/routes \
-H "Authorization: Bearer whr_your_api_key" \
-d '{"name": "Square Payments", "sourceId": "src_...", "destinationIds": ["dst_..."]}'
# Create route for order events
curl -X POST https://api.hookbase.app/api/routes \
-H "Authorization: Bearer whr_your_api_key" \
-d '{"name": "Square Orders", "sourceId": "src_...", "destinationIds": ["dst_..."]}'Signature Verification
Square signs webhooks using HMAC-SHA256. The signature is sent as a base64-encoded value in the x-square-hmacsha256-signature header.
x-square-hmacsha256-signature: GF4YkfnJpUKBl7AoP9ND9g==Hookbase automatically verifies this when configured:
{
"verificationConfig": {
"type": "hmac",
"secret": "your-square-signature-key",
"algorithm": "sha256",
"header": "x-square-hmacsha256-signature",
"encoding": "base64"
}
}Signature Computation
Square computes the HMAC-SHA256 signature over the notification URL concatenated with the request body. This means the signed content is:
notification_url + raw_request_bodyThis is different from most webhook providers that sign only the request body. If you are verifying signatures manually, ensure you prepend your Hookbase webhook URL to the raw body before computing the HMAC. Hookbase handles this automatically when you configure the source with the hmac verification type and your Square signature key.
TIP
If signature verification fails after changing your webhook URL, make sure the notification URL registered in Square matches the URL Hookbase uses for verification. The URL is part of the signed content.
Common Events
payment.completed
Triggered when a payment is successfully completed.
{
"merchant_id": "MLF2Z4SFOAXBM",
"type": "payment.completed",
"event_id": "d9b80b48-b8a0-4cf6-96a8-2b7e8a5e3c1d",
"created_at": "2026-03-07T14:30:00.000Z",
"data": {
"type": "payment",
"id": "KkAkhdMsgzn59SM8A4U3sB7MN3F",
"object": {
"payment": {
"id": "KkAkhdMsgzn59SM8A4U3sB7MN3F",
"created_at": "2026-03-07T14:29:45.000Z",
"updated_at": "2026-03-07T14:30:00.000Z",
"amount_money": {
"amount": 1500,
"currency": "USD"
},
"total_money": {
"amount": 1500,
"currency": "USD"
},
"status": "COMPLETED",
"source_type": "CARD",
"card_details": {
"status": "CAPTURED",
"card": {
"card_brand": "VISA",
"last_4": "5858",
"exp_month": 12,
"exp_year": 2027
},
"entry_method": "EMV"
},
"location_id": "LFR7EH5G3XANT",
"order_id": "nPFDY8oPxdeALHQPxIuSHBnqRFo",
"customer_id": "VDKXEEKPJN48QDG3BGGFAK05P8"
}
}
}
}order.created
Triggered when a new order is created.
{
"merchant_id": "MLF2Z4SFOAXBM",
"type": "order.created",
"event_id": "a8b7c6d5-e4f3-2a1b-9c8d-7e6f5a4b3c2d",
"created_at": "2026-03-07T14:28:00.000Z",
"data": {
"type": "order",
"id": "nPFDY8oPxdeALHQPxIuSHBnqRFo",
"object": {
"order_created": {
"created_at": "2026-03-07T14:28:00.000Z",
"order_id": "nPFDY8oPxdeALHQPxIuSHBnqRFo",
"location_id": "LFR7EH5G3XANT",
"state": "OPEN",
"version": 1
}
}
}
}invoice.payment_made
Triggered when a payment is made on an invoice.
{
"merchant_id": "MLF2Z4SFOAXBM",
"type": "invoice.payment_made",
"event_id": "c2d3e4f5-a6b7-8c9d-0e1f-2a3b4c5d6e7f",
"created_at": "2026-03-07T15:00:00.000Z",
"data": {
"type": "invoice",
"id": "inv_0JRTYxBKbYQZfURA2p",
"object": {
"invoice": {
"id": "inv_0JRTYxBKbYQZfURA2p",
"version": 3,
"location_id": "LFR7EH5G3XANT",
"order_id": "nPFDY8oPxdeALHQPxIuSHBnqRFo",
"payment_requests": [
{
"request_type": "BALANCE",
"due_date": "2026-03-15",
"total_completed_amount_money": {
"amount": 5000,
"currency": "USD"
}
}
],
"status": "PAID",
"primary_recipient": {
"customer_id": "VDKXEEKPJN48QDG3BGGFAK05P8"
}
}
}
}
}customer.created
Triggered when a new customer is created.
{
"merchant_id": "MLF2Z4SFOAXBM",
"type": "customer.created",
"event_id": "f1e2d3c4-b5a6-9788-7c6d-5e4f3a2b1c0d",
"created_at": "2026-03-07T12:00:00.000Z",
"data": {
"type": "customer",
"id": "VDKXEEKPJN48QDG3BGGFAK05P8",
"object": {
"customer": {
"id": "VDKXEEKPJN48QDG3BGGFAK05P8",
"created_at": "2026-03-07T12:00:00.000Z",
"updated_at": "2026-03-07T12:00:00.000Z",
"given_name": "Jane",
"family_name": "Doe",
"email_address": "[email protected]",
"phone_number": "+15555551234",
"preferences": {
"email_unsubscribed": false
}
}
}
}
}inventory.count.updated
Triggered when inventory counts change.
{
"merchant_id": "MLF2Z4SFOAXBM",
"type": "inventory.count.updated",
"event_id": "e7f8a9b0-c1d2-3e4f-5a6b-7c8d9e0f1a2b",
"created_at": "2026-03-07T16:45:00.000Z",
"data": {
"type": "inventory",
"id": "adjustment_1a2b3c4d5e6f",
"object": {
"inventory_counts": [
{
"catalog_object_id": "W62UWFY35CWMYGVWK6TWJDNI",
"catalog_object_type": "ITEM_VARIATION",
"location_id": "LFR7EH5G3XANT",
"quantity": "42",
"state": "IN_STOCK",
"calculated_at": "2026-03-07T16:45:00.000Z"
}
]
}
}
}Transform Examples
Slack Alert for Payments
function transform(payload) {
const payment = payload.data.object.payment;
const amount = (payment.amount_money.amount / 100).toFixed(2);
const currency = payment.amount_money.currency;
const cardBrand = payment.card_details?.card?.card_brand || "N/A";
const last4 = payment.card_details?.card?.last_4 || "****";
return {
blocks: [
{
type: "header",
text: {
type: "plain_text",
text: "Square Payment Received"
}
},
{
type: "section",
fields: [
{
type: "mrkdwn",
text: `*Amount:*\n${currency} $${amount}`
},
{
type: "mrkdwn",
text: `*Status:*\n${payment.status}`
},
{
type: "mrkdwn",
text: `*Card:*\n${cardBrand} ending in ${last4}`
},
{
type: "mrkdwn",
text: `*Location:*\n${payment.location_id}`
}
]
},
{
type: "context",
elements: [
{
type: "mrkdwn",
text: `Payment ID: ${payment.id} | Order: ${payment.order_id || "N/A"}`
}
]
}
]
};
}Database Format
function transform(payload) {
const event = payload;
const data = event.data;
// Handle payment events
if (event.type.startsWith("payment.")) {
const payment = data.object.payment;
return {
square_event_id: event.event_id,
event_type: event.type,
merchant_id: event.merchant_id,
object_id: payment.id,
object_type: "payment",
customer_id: payment.customer_id || null,
order_id: payment.order_id || null,
location_id: payment.location_id,
amount: payment.amount_money.amount,
currency: payment.amount_money.currency,
status: payment.status,
source_type: payment.source_type,
created_at: event.created_at
};
}
// Handle order events
if (event.type.startsWith("order.")) {
const order = data.object.order_created || data.object.order_updated;
return {
square_event_id: event.event_id,
event_type: event.type,
merchant_id: event.merchant_id,
object_id: order.order_id,
object_type: "order",
location_id: order.location_id,
state: order.state,
version: order.version,
created_at: event.created_at
};
}
// Generic fallback
return {
square_event_id: event.event_id,
event_type: event.type,
merchant_id: event.merchant_id,
object_id: data.id,
object_type: data.type,
created_at: event.created_at,
raw_data: data.object
};
}Alert for High-Value Payments
function transform(payload) {
const payment = payload.data.object.payment;
const amount = (payment.amount_money.amount / 100).toFixed(2);
const currency = payment.amount_money.currency;
return {
channel: "#sales-alerts",
username: "Square Bot",
icon_emoji: ":money_with_wings:",
attachments: [{
color: "good",
title: "High-Value Payment Completed",
fields: [
{ title: "Amount", value: `${currency} $${amount}`, short: true },
{ title: "Status", value: payment.status, short: true },
{ title: "Source", value: payment.source_type, short: true },
{ title: "Location", value: payment.location_id, short: true },
{ title: "Customer", value: payment.customer_id || "Guest", short: true },
{ title: "Order", value: payment.order_id || "N/A", short: true }
],
footer: `Payment ${payment.id}`,
ts: Math.floor(Date.now() / 1000)
}]
};
}Filter Examples
Payment Events Only
{
"name": "Payment Events",
"logic": "or",
"conditions": [
{
"field": "type",
"operator": "starts_with",
"value": "payment."
}
]
}Order and Fulfillment Events
{
"name": "Order Events",
"logic": "or",
"conditions": [
{
"field": "type",
"operator": "starts_with",
"value": "order."
}
]
}Completed Payments Only
{
"name": "Completed Payments",
"logic": "and",
"conditions": [
{
"field": "type",
"operator": "equals",
"value": "payment.completed"
}
]
}High-Value Payments ($100+)
{
"name": "High Value ($100+)",
"logic": "and",
"conditions": [
{
"field": "type",
"operator": "equals",
"value": "payment.completed"
},
{
"field": "data.object.payment.amount_money.amount",
"operator": "greater_than",
"value": "10000"
}
]
}Specific Merchant
{
"name": "Main Store Only",
"logic": "and",
"conditions": [
{
"field": "merchant_id",
"operator": "equals",
"value": "MLF2Z4SFOAXBM"
}
]
}Headers
Square includes the following headers with webhook notifications:
| Header | Description | Example |
|---|---|---|
x-square-hmacsha256-signature | HMAC-SHA256 signature (base64-encoded) | GF4YkfnJpUKBl7AoP9ND9g== |
content-type | Always application/json | application/json |
user-agent | Square webhook user agent | Square-Webhooks |
x-square-event-id | Unique event identifier | d9b80b48-b8a0-4cf6-96a8-2b7e8a5e3c1d |
Testing
Using Square Sandbox
Square provides a sandbox environment for testing:
- Create a separate Hookbase source for sandbox:
curl -X POST https://api.hookbase.app/api/sources \
-H "Authorization: Bearer whr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Square Sandbox",
"slug": "square-sandbox",
"verificationConfig": {
"type": "hmac",
"secret": "your-sandbox-signature-key",
"algorithm": "sha256",
"header": "x-square-hmacsha256-signature",
"encoding": "base64"
}
}'- In the Square Developer Dashboard, create a webhook subscription pointing to your sandbox source URL
- Use the Square Sandbox test account to trigger events (create payments, orders, etc.)
Best Practices
Separate sandbox and production: Use different Hookbase sources for sandbox and production webhook subscriptions
Handle idempotency: Use
event_idto prevent duplicate processing, as Square may redeliver eventsMonitor signature failures: Failed signature verifications may indicate a mismatch between the notification URL registered in Square and the one Hookbase uses, or an incorrect signature key
Track merchant context: Square payloads include
merchant_idwhich is useful for multi-location or multi-merchant setupsHandle retries: Square retries failed webhook deliveries with exponential backoff; ensure your handlers are idempotent
Use event versions: Some Square events include version numbers on objects -- use these to handle out-of-order delivery
Troubleshooting
Signature Verification Failed
- Verify you are using the Signature key from the webhook subscription, not your application API key
- Ensure the notification URL registered in Square exactly matches your Hookbase webhook URL -- Square includes the URL in the signed content
- Check that the signature key corresponds to the correct environment (sandbox vs production)
- Make sure the source
verificationConfiguses"encoding": "base64"since Square sends base64-encoded signatures
Missing Events
- Check which events are subscribed in the Square Developer Dashboard under your webhook subscription
- Verify your filters are not blocking expected events
- Check the Square Developer Dashboard webhook logs for delivery attempts and errors
- Ensure your webhook subscription status is Active
Duplicate Events
- Square may retry failed deliveries up to several times with exponential backoff
- Implement idempotency using the
event_idfield from the payload - Check for multiple webhook subscriptions pointing to the same or similar endpoints
URL Mismatch Errors
- Square computes signatures using the notification URL + request body
- If you change your Hookbase webhook URL, you must update the subscription in the Square Developer Dashboard
- Trailing slashes matter -- ensure the URL matches exactly