- Beta Orders from your WooCommerce or Shopify store
Send orders from your store
A connection lets your own shop — WooCommerce, Shopify, or any system that can sign a webhook — tell XQUARE when something happens, such as a new or updated order. XQUARE checks the signature, records the event in your Space's event log with its outcome, and never stores your customers' email addresses or phone numbers in the clear.
The inbound endpoint is live. The WooCommerce and Shopify steps below have not yet been run end to end against a real store; check the event log on the Connections page after your first order.
Create a connection
In the vendor panel, open Connections and add a connection for your platform. Each connection has:
- an endpoint URL that ends in the connection's key, and
- a signing secret, shown when you create or rotate it.
Treat both as secrets. Anyone holding the URL and the secret can post events as your store.
WooCommerce
In WooCommerce → Settings → Advanced → Webhooks, add a webhook:
| Field | Value |
|---|---|
| Delivery URL | the endpoint URL from the Connections tab (the key is the last path segment) |
| Secret | the connection's signing secret |
| Topic | the order topic you want, for example Order updated |
| API version | the current WooCommerce REST API version |
XQUARE verifies X-WC-Webhook-Signature (base64 HMAC-SHA256 of the body) and reads the topic
from X-WC-Webhook-Topic. A repeated delivery of the same order is recognised by the order id
in the body, because WooCommerce gives each retry a new delivery id.
Shopify
In Settings → Notifications → Webhooks (or your app's webhook subscription), point the order topic at the endpoint URL from the Connections tab, and use the connection's secret as the signing secret.
XQUARE verifies X-Shopify-Hmac-Sha256 (base64 HMAC-SHA256 of the raw body), reads the topic
from X-Shopify-Topic, deduplicates on X-Shopify-Webhook-Id, and checks the clock against
X-Shopify-Triggered-At. The window is long enough to admit Shopify's own retry schedule
(5 hours on the live profile as of 2026-10-18); an older or missing value is recorded as
replay_rejected.
Neither WooCommerce nor Shopify can add a custom header, which is why the key travels in the URL for these two platforms. A native connection does not accept the key in a header.
Any other system (generic)
POST JSON to the endpoint and sign it yourself:
| Header | Value |
|---|---|
X-XQ-Key | the connection's key (or put it as the last path segment of the URL) |
X-XQ-Timestamp | Unix seconds when you signed the request |
X-XQ-Signature | hex HMAC-SHA256 of {timestamp}.{raw body} with the signing secret |
Requests whose timestamp is more than a few minutes off are recorded as replay_rejected.
Send a stable external_id (or id / order_id) in the body so a retry is recognised as a
duplicate.
The exact skew window for the generic profile is profile data, not code; confirm it in the Connections tab before you rely on a figure.
What the endpoint answers
| Status | Meaning |
|---|---|
202 | The signature is good and the event was recorded. The body's outcome says what happened. |
200 with outcome: "duplicate" | XQUARE already has this delivery; nothing new was written. |
401 | Bad signature, unknown key, or the connection is disconnected. |
503 | XQUARE could not process the request right now. Your platform will retry. |
Once the signature passes, XQUARE always answers 2xx, so your platform does not switch the
webhook off. Problems are recorded as outcomes in the event log instead:
| Outcome | Meaning |
|---|---|
filtered | The connection's filter excludes this event (for example an order still pending). |
replay_rejected | The timestamp was missing, unreadable or too old. |
rejected | The body was not JSON, or the connection sent more than its per-minute limit. |
no_identity | The order carries no email, or no XQUARE customer matches it. |
no_target_key | Recorded. No perk is granted from external orders yet. |
Privacy
Before an event is stored, email addresses and phone numbers anywhere in the payload, including inside free text, are replaced by a salted hash. Event log rows are deleted 90 days after they were received (a daily purge job).
Disconnect
Disconnecting a connection in the Connections tab stops it at once: later deliveries get 401
and are logged as disconnected.