Webhooks

Webhooks send a JSON POST to your server when something happens in a wishlist. Add endpoints in the app under Developers → Webhooks.

Events

EventWhen
item.addedA shopper or staff member saved an item.
item.removedAn item was removed from a list.
list.createdA shopper created a list.
list.sharedA shopper shared a list.
back_in_stock.triggeredA back in stock alert was sent for a product.
price_drop.triggeredA price drop alert was sent for a product.

Payload

{
  "id": "evt_…",
  "event": "item.added",
  "occurredAt": 1767225600000,
  "shop": "example.myshopify.com",
  "data": {
    "productId": "123",
    "variantId": "456",
    "listId": "…"
  }
}

Fields inside data by event:

Eventdata
item.addedproductId, variantId, listId, customerId, addedBy, locationId
item.removedproductId, variantId, listId, customerId
list.createdlistId, name, kind, customerId
list.sharedlistId, customerId, itemCount
back_in_stock.triggeredproductId, variantId, quantity
price_drop.triggeredproductId, variantId, oldPrice, newPrice, currency

customerId is null for guests. variantId is null for product-level items. Prices are decimal strings in the shop currency.

Use id to ignore duplicates: the same event can arrive more than once.

Verifying the signature

Each request has an X-Wishlist-Signature header. It is the hex-encoded HMAC-SHA256 of the raw request body, using your endpoint's signing secret. The secret is shown once, when you add the endpoint.

Always compute the HMAC over the raw bytes, before parsing JSON, and compare in constant time.

import crypto from "node:crypto";

export function verify(rawBody, signature, secret) {
  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(signature ?? "", "hex");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Responding and retries

  • Respond with any 2xx status within 10 seconds. Redirects are not followed.
  • Other statuses, timeouts and network errors are retried up to 8 attempts in total, starting 30 seconds after the first try and doubling each time (about an hour in all).
  • Every attempt is listed under Developers → Deliveries for 90 days.

Endpoint rules

  • URLs must use https:// and be publicly reachable. Private IP ranges, localhost and link-local addresses are rejected.
  • Up to 10 endpoints per store.
  • Pause an endpoint to stop deliveries without deleting it.