Skip to main content

Overview

Instead of polling, your app can receive webhooks — Salesive sends an HTTP POST to your endpoint whenever something changes on a store your app is installed on (an order is created, a product updated, and so on).
A webhook is a notification, not the data. A delivery tells you what changed — the resource, the action, and the affected record’s id — but it does not include the record’s fields. Fetch the current state yourself from the Apps API with your app token (see Fetch the current data). Reading it back through your own scoped token means you only ever receive data your permissions allow, and you always act on the latest state rather than a possibly-stale snapshot.
Webhooks are permission-gated: you only receive events for a resource if the merchant granted your app the scope that governs it (e.g. you get orders/* events only if the installation has READ_ORDERS). See Scopes & permissions.

Enable webhooks

Set a Webhook URL on your app in the dashboard Developer console (it’s optional — leave it blank to disable). The URL must be HTTPS and publicly reachable. When you save it, the console shows your app’s signing secret (whsec_…) — you’ll use it to verify deliveries.
One webhook URL serves all installations of your app. Each delivery tells you which store it’s for (shopId in the body and the X-Salesive-Shop-Id header), so route by that.

The request

Every delivery is a POST with a JSON body:
There is no record data in the payload — only the identifiers you need to fetch it.

Headers

Topics

topic is resource/action. For admin events the action is created (POST), updated (PUT/PATCH), or deleted (DELETE). Storefront events (customer activity on the live store) carry their own action (signed_in, placed, paid, …). You receive a topic only if your installation holds the scope below.

Admin events (dashboard / API changes)

Storefront events (live shopper activity)

These fire from the storefront when a shopper acts on the live store.
orders/placed and orders/paid are distinct from the admin orders/created/orders/updated. A single storefront checkout fires orders/placed (then later orders/paid) — never the admin topics. Use the resourceId (the order id) with READ_ORDERS to fetch the order via the Apps API.
READ_CARTS and the wishlists/* topics are notify-only with no fetch API — the envelope carries the cart/wishlist resourceId and shopId, but there is no endpoint to read cart or wishlist contents. They’re intended as triggers (e.g. abandoned-cart timing), not data sources.
Events fire for successful changes regardless of who made them — a merchant editing an order in the dashboard, another app, or a shopper on the storefront. Dedupe on id and treat delivery as at-least-once.

Fetch the current data

The payload carries no record fields — when you receive an event, read the current state from the Apps API using your app access token and the resource + resourceId:
Re-fetching is deliberate, and safer than a fat payload:
  • You only ever see data you’re entitled to. The record comes back through your scoped token, so the webhook can never hand you fields your permissions don’t cover.
  • You always act on the latest state, not a snapshot that may be stale by the time you process it.
On a deleted event the record is gone — don’t re-fetch; act on resource + resourceId. When resourceId is absent (bulk or sub-actions), re-list the affected resource to re-sync the collection.

Verify the signature

Every delivery is signed so you can trust it came from Salesive and wasn’t altered. Compute the base64 HMAC-SHA256 of the raw request body using your app’s signing secret, and compare it to the X-Salesive-Hmac-SHA256 header with a constant-time check.
Verify against the raw request body bytes — exactly as received, before any JSON parsing or re-serialization. Re-stringifying the parsed object will change the bytes and break verification.
Node.js (Express)

Respond & retries

  • Acknowledge fast. Return a 2xx within a few seconds. Do heavy work asynchronously — Salesive treats anything other than 2xx (or a timeout) as a failed delivery.
  • Retries. A failed delivery is retried with exponential backoff for several attempts before it’s dropped. Because retries reuse the same id, make your handler idempotent (dedupe on id).
  • At-least-once. You may occasionally receive a duplicate; never assume exactly-once.

Best practices

  • Verify the signature on every request; reject unsigned or mismatched deliveries.
  • Process asynchronously (queue the event, ack immediately).
  • Scope your handling to the shopId — a delivery only ever concerns one store.
  • Don’t depend on ordering; use occurredAt if you need to reason about sequence.