Webhooks
Webhooks let xPage push real-time events to your app when resources change — no polling required. You configure a webhook URL and the topics you care about from the developer platform; xPage sends a signed HTTP POST for each matching event.
Topics
| Topic | Fires when |
|---|---|
product.created | A product is created |
product.updated | A product is updated |
order.created | An order is placed |
order.updated | An order is updated |
customer.created | A customer is created |
customer.updated | A customer is updated |
review.created | A review is submitted |
Payload
Every webhook is a POST request with a JSON body:
{
"topic": "order.created",
"install_id": "uuid",
"shop_domain": "store.myxpage.com",
"data": { ... }
}
| Field | Description |
|---|---|
topic | The event topic (e.g. order.created) |
install_id | The app install this event belongs to |
shop_domain | Primary domain of the hosting |
data | Full resource object for the affected entity |
Verifying requests
Every webhook includes an X-XPage-Signature header. Always verify it before processing — discard any request that fails.
Headers sent with every webhook:
| Header | Value |
|---|---|
X-XPage-Topic | The event topic (e.g. order.created) |
X-XPage-Signature | sha256=<hmac> |
X-XPage-Shop-Domain | Primary domain of the hosting |
Verification:
sha256=HMAC-SHA256(raw_json_body, signing_secret)
Compute the HMAC over the raw request body using your signing_secret, prepend sha256=, and compare with the header value using a constant-time comparison.
Parse the JSON only after verification. Any re-serialization may change byte ordering and break the signature check.
Example (Node.js):
const crypto = require('crypto')
function verifyWebhook(rawBody, signatureHeader, signingSecret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', signingSecret)
.update(rawBody)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
)
}
Delivery & retries
xPage delivers webhooks asynchronously. If your endpoint does not return a 2xx response within 10 seconds, the delivery is retried on an exponential schedule:
| Attempt | Retry delay |
|---|---|
| 1 | 1 minute |
| 2 | 1 hour |
| 3–9 | Once per day (up to 7 days) |
After all retries are exhausted the delivery is dropped. Build your endpoint to be idempotent — the same event may be delivered more than once.
Return 200 immediately and process the payload asynchronously. Slow endpoints risk timeouts and unnecessary retries.