Skip to main content

Webhooks

Webhooks allow your app to receive real-time notifications when specific events occur within a SitePack store. Instead of polling the API for changes, SitePack will "push" data to your server via an HTTPS POST request.

How Webhooks Work​

  1. Subscription: You define the events (topics) you want to listen to in your app.json.
  2. Event Occurs: A merchant performs an action (e.g., a customer submits a form).
  3. Delivery: SitePack sends a signed JSON payload to your configured endpoint.
  4. Acknowledgement: Your server must respond with a 200 OK status code to acknowledge receipt.

The "Thin Payload" Strategy​

To ensure security and data privacy, SitePack webhooks use a Thin Payload architecture. Instead of sending all the resource data (which might contain sensitive PII), the webhook contains only the topic, the site and the uuid of the record that changed.

Example Payload​

{
"uuid": "019eca47-221d-73c1-9c6c-c556a2b02cc2",
"test_mode": true,
"topic": "lead.created",
"site": "019eca47-21a4-7935-8636-641e7370897b",
"created_at": "2026-06-15T07:55:19Z",
"arguments": {
"test": true,
"timestamp": 1781510118,
"message": "This is a test webhook message from the admin panel."
}
}

After receiving this notification, your app should call the API using its credentials to fetch the full data. This ensures that:

  • Sensitive data is never sent over the wire unnecessarily.
  • The App's Scopes are re-validated before data access.

Webhook topics​

Every event carries the uuid of the record that changed in arguments.uuid. Read the record with the endpoint in the last column, using your own scopes. See API Endpoints for a worked example.

Leads and customers​

TopicWhen it firesRead it with
lead.createdA visitor submits a form on the site.GET /api/public/v1/leads/{uuid} (leads:read)
lead.updatedA lead's status, rating or notes change.GET /api/public/v1/leads/{uuid} (leads:read)
lead.completedA lead is closed as won or lost. Fires after lead.updated.GET /api/public/v1/leads/{uuid} (leads:read)
lead.deletedA lead is deleted.Nothing to read: remove your copy.
customer.createdA customer record is created, at checkout or through the API.GET /api/public/v1/customers/{uuid} (customers:read)
customer.updatedA customer record changes.GET /api/public/v1/customers/{uuid} (customers:read)
newsletter.subscribedSomeone signs up for the newsletter.No endpoint yet.
newsletter.confirmedA newsletter sign-up is confirmed.No endpoint yet.
newsletter.unsubscribedSomeone unsubscribes.No endpoint yet.
comment.updatedA comment or review is moderated.GET /api/public/v1/comments (comments:read)
comment.deletedA comment or review is deleted.Nothing to read: remove your copy.

Webshop​

TopicWhen it firesRead it with
order.createdAn order is placed, before online payment completes.GET /api/public/v1/orders/{uuid} (orders:read)
order.paidAn order's payment reaches "completed".GET /api/public/v1/orders/{uuid} (orders:read)
product.createdA product is created.GET /api/public/v1/products/{uuid} (products:read)
product.updatedA product's details change.GET /api/public/v1/products/{uuid} (products:read)
product.deletedA product is deleted.Nothing to read: remove your copy.
product.stock_adjustedA product's stock is adjusted.GET /api/public/v1/products/{uuid}/stock (inventory:read)
product.option_updatedA product's options (such as size or colour) change.GET /api/public/v1/products/{uuid}/options (products:read)
product.variant_updatedA product's variants change (sku, ean, price, stock, image, properties).GET /api/public/v1/products/{uuid}/variants (products:read)
product.variant_deletedA product variant is removed.GET /api/public/v1/products/{uuid}/variants (products:read)
collection.createdA collection is created.GET /api/public/v1/collections/{uuid} (collections:read)
collection.updatedA collection or its rules change.GET /api/public/v1/collections/{uuid} (collections:read)
collection.deletedA collection is deleted.Nothing to read: remove your copy.

For every product.* topic, arguments.uuid is the uuid of the product, also when a variant or option changed. See Product Options & Variants.

Content​

TopicWhen it firesRead it with
content.created, content.updatedA page is created or changed.GET /api/public/v1/content/{uuid} (content:read)
content.published, content.unpublishedA page goes online or offline.GET /api/public/v1/content/{uuid} (content:read)
content.deletedA page is deleted.Nothing to read: remove your copy.
blog.created, blog.updatedA blog post is created or changed.GET /api/public/v1/blogs/{uuid} (blogs:read)
blog.deletedA blog post is deleted.Nothing to read: remove your copy.
category.created, category.updatedA category is created or changed.GET /api/public/v1/categories/{uuid} (categories:read)
category.deletedA category is deleted.Nothing to read: remove your copy.
tag.created, tag.updatedA tag is created or changed.GET /api/public/v1/tags/{uuid} (tags:read)
tag.deletedA tag is deleted.Nothing to read: remove your copy.
media.updatedA media item's details change.GET /api/public/v1/media/{uuid} (media:read)
media.deletedA media item is deleted.Nothing to read: remove your copy.
redirect.created, redirect.updatedA redirect is created or changed.GET /api/public/v1/redirects/{uuid} (redirects:read)
redirect.deletedA redirect is deleted.Nothing to read: remove your copy.
translation.updated, translation.deletedA translation is saved or removed. arguments.uuid is the translation's own uuid.Translations (translations:read)
elements.updatedThe homepage, footer or sidebar elements change. arguments.uuid is the site uuid.GET /api/public/v1/elements/{area} (elements:read)
usp.updatedA USP is created or changed.GET /api/public/v1/usps/{uuid} (usp:read)
topbar.updatedThe topbar changes.No endpoint yet.
event.created, event.updated, event.deletedAn agenda event is created, changed or deleted.No endpoint yet.

Site​

TopicWhen it firesRead it with
site.updatedThe site settings change.GET /api/public/v1/site (sites:read)
module.settings_updatedA module's settings change. arguments.uuid is the site uuid.GET /api/public/v1/modules (modules:read)
app.installed, app.uninstalled, app.settings_updatedYour own app is installed, removed or reconfigured. See below.GET /api/public/v1/app/settings (app:settings:read)

Topics that do not fire yet​

GET /api/public/v1/webhooks lists a few more topics as available. You can subscribe to them, but SitePack does not send them yet: order.updated, product.option_created, product.option_deleted, product.variant_created, discount.created, discount.archived, navigation.updated, shipping.label_created, mailbox.created, mailbox.deleted and domain.ordered. Until they do, poll the matching list endpoint, or use a related topic: a new variant also fires product.variant_updated.

App lifecycle topics​

The app.* topics are about your app itself rather than about store data. Unlike the other topics, which go to every active app subscribed to them, an app.* event is delivered only to the app it concerns. Subscribe to them in the webhooks array of your app.json like any other topic; they are signed with your App Secret in the same way.

TopicWhen it fires
app.installedOn a fresh install of your app on a site. Re-approving an app that is already active (for example after a new version asks for more scopes) does not fire it again.
app.uninstalledAfter the merchant uninstalled your app. It is still delivered to you even though your app is no longer active on the site, so you can clean up.
app.settings_updatedEvery time the merchant saves your app's settings page in the admin.

The payload is thin, like every other webhook: site is the site uuid and arguments.uuid is your app's uuid.

{
"uuid": "019f4a1c-5e2b-7d10-8a3e-2f6c9b1d0e47",
"test_mode": false,
"topic": "app.settings_updated",
"site": "019eca47-21a4-7935-8636-641e7370897b",
"created_at": "2026-10-01T09:12:44Z",
"arguments": {
"uuid": "019e8b20-3c4d-7a51-9f62-7d8e1a2b3c4d"
}
}

A typical use of app.installed and app.settings_updated is to read the merchant's settings back with GET /api/public/v1/app/settings (merchant_settings, scope app:settings:read), so a changed connection or password setting is picked up right away.

Security and Verification​

HTTPS Requirement​

All webhook addresses must use the https:// protocol.

Signing (HMAC)​

Every webhook request is signed with your Webhook signing secret using HMAC-SHA384. You find it in the Partner Portal on your app's page, in the OAuth & install card, where you can also rotate it. Verify it to be sure the request truly came from SitePack and was not altered.

Each request carries two headers:

HeaderValue
X-SitePack-Webhook-TimestampThe unix time SitePack signed the request.
X-SitePack-Webhook-Signaturev1= followed by the hex HMAC-SHA384 digest.

The signature is computed over "{timestamp}.{raw_body}". To verify, recompute the HMAC over the raw request body exactly as received (do not re-serialize the JSON) and compare in constant time. Reject a request whose timestamp is too old to blunt replay attacks.

import hmac, hashlib

def is_valid(secret, timestamp, raw_body, header):
expected = "v1=" + hmac.new(
secret.encode(),
f"{timestamp}.{raw_body}".encode(),
hashlib.sha384,
).hexdigest()
return hmac.compare_digest(expected, header)

Delivery and retries​

  • SitePack posts each event once, with a 5 second connect timeout. Answer with a 2xx quickly and do the real work afterwards, for example in a queue.
  • When your server does not answer with a 2xx, or cannot be reached, SitePack retries once, about 5 seconds later. After that the event is dropped.
  • A retry is a new delivery with a new event uuid. Make your handler safe to run twice: key your work on topic plus arguments.uuid, not on the event uuid.
  • Webhooks are a signal, not a log. If you must not miss anything, also sync from the list endpoints now and then, for example GET /api/public/v1/leads, which lists the most recently changed leads first.

Configuring Webhooks​

Webhooks are part of an API integration: your server needs the signing secret to verify them and an access token to read the data behind them.

Add the webhooks array to your app.json and run sitepack app:publish:

{
"webhooks": [
{
"topic": "lead.created",
"address": "https://your-app.com/webhooks/leads"
}
]
}

Once published, your app's page in the Partner Portal lists the subscriptions under App Webhooks. Click Test next to a topic to send a test message (with "test_mode": true) to your address.

Next Steps​