Webhook Topics
Webhook topics notify your app when events occur in the merchant’s store. Subscribe to a topic withPOST /api/v1/webhooks.json
to receive real-time notifications. (Subscribing requires the read_shop
scope — there is no dedicated webhook write scope.)
This is a static reference page — there is no
GET .../webhooks/topics.json
endpoint to query the list at runtime. The subscribable topics are fixed by
the API and listed below. Subscribing to a value that is not in this list is
rejected with a 422 validation error.POST /api/v1/webhooks.json.
Order, product, customer, collection and refund webhooks are delivered as
snake_case bodies (
line_items, financial_status,
money fields as decimal strings) — the same shape as the corresponding REST
resource. The contract version is sent in the X-LMS-Api-Version header
(currently 2026-06), and the sending store is identified by the
X-LMS-Shop-Domain header.Available Topics
Orders
orders/create payload
The body is the order in standard commerce shape (snake_case,line_items,
financial_status, money as decimal strings), plus a top-level
shipping_lines[] carrying the source routing key. The sending store is
identified by the X-LMS-Shop-Domain header (the order body has no
storeId).
Field notes for order webhooks
- Money fields are decimal strings (
"49.00"), not numbers. line_items[].variant_idfalls back toproduct_idfor products with no variants.- There is no
digitalflag on the order — drive digital/course fulfilment offline_items[].product_id. - The order body has no
storeId; the sending store is theX-LMS-Shop-Domainrequest header.
shipping_lines[].source is the routing key for shipping apps.
When a merchant has multiple shipping apps installed, every app
receives every orders/create webhook. Each app inspects source
against its own handle and only acts when it matches — this is how
your app distinguishes “the customer picked MY rate” from “the
customer picked another app’s rate”. See
Live Rate Providers
for the full handler pattern.
source values:
"<app_handle>"(e.g."shiprocket") — customer picked this app’s live rate at checkoutnull— customer picked a merchant ShippingZone or a local-delivery rule (no app should auto-push)
code carries your app’s service_code from the live-rate
response. Convention is <app_handle>-<carrier_id> so apps can strip
the prefix to recover their carrier’s internal identifier.
Refunds
Detecting cancellations & refunds reliably. A merchant cancel emits
orders/updated + orders/cancelled; a full refund emits refunds/createorders/updated+orders/cancelled; a POS refund emitsrefunds/create. Delivery is at-least-once and unordered, so don’t count on an exact number of signals — on any cancellation/refund event, re-fetch the order (GET /api/v1/orders/{id}.json, scoperead_orders) and reconcile against the authoritativeorder.status. Make your entitlement changes idempotent.
Products
Customers
Inventory
Fulfillments
Subscriptions
Recurring product subscriptions sold through selling plans. The underlying recurring billing is handled by the merchant’s Stripe subscription; these topics fire as the Stripe subscription moves through its lifecycle.There is no separate
subscriptions/expired topic. An expired
subscription ends as customer.subscription.deleted in Stripe and is
delivered as subscriptions/cancelled.Lifecycle order
A typical subscription fires these topics in order over its lifetime:subscriptions/renew and subscriptions/payment_failed can repeat for the
life of the subscription. subscriptions/cancelled is terminal — no further
events fire for that subscription afterwards.
subscription payload
All five subscription topics share the same payload shape:status mirrors the underlying subscription status. The value you can
expect per topic:
Example handler
Grant access on the first charge and each renewal, pause on a failed charge, and revoke on cancellation:Idempotency & ordering: deliveries are at-least-once and may arrive out
of order or be retried, so key your handler on
subscriptionId +
currentPeriodStart and make access changes idempotent. The topic is in the
X-LMS-Topic header; verify the X-LMS-Hmac-SHA256 signature (see
Webhook Verification) before acting.App Lifecycle
app_subscriptions/update payload:
billingStatus—active|trial|past_due|cancelled|free.status— the installation state:active(app functional) ordisabled(API tokens, session tokens and extensions all stop working).graceEndsAt— present on payment failure: the app keeps full service until this time (7-day grace). A successful retry inside the window restoresbillingStatus: activeautomatically.reason— present onbilling_grace_expired(the daily sweep disabled the install) andcancel_scheduled(merchant cancelled; service continues until the paid period ends, then the install is disabled).
A later successful payment (e.g. the merchant re-subscribes or fixes their
card) re-enables a disabled installation automatically — you’ll receive
another
app_subscriptions/update with billingStatus: active,
status: active. Don’t delete merchant data on disable; treat it as a
pause.Platform-dispatched topics (no subscription)
The following topics are not subscribable viaPOST /api/v1/webhooks.json
— they are delivered automatically to the webhook URLs configured on your
app, independent of any subscription.
GDPR (Mandatory)
GDPR webhooks are mandatory for app-store listing. See GDPR compliance for the payload contract and deadlines.
Subscribing to Webhooks
Create a webhook subscription using the API:Webhook Subscription Parameters
string
required
The webhook topic to subscribe to. Must be one of the subscribable topics listed above.
string
required
HTTPS URL where webhook payloads are POSTed.
string
default:"json"
Payload format:
json (default) or xml.Webhook Delivery
Your endpoint must return a
2xx status code within 10 seconds to acknowledge receipt. Anything else is treated as a failure and retried per the policy above. After 3 failed attempts the delivery is moved to the delivery-log error state — inspect via Webhook Delivery Logs.
See Webhook Verification for the HMAC contract.
Listing Subscriptions
Deleting a Subscription
Secret & lifecycle
- Each subscription has its own HMAC signing secret, returned once in the
POST /api/v1/webhooks.jsoncreate response. Store it securely — it is not included when you list subscriptions. - There is no update or secret-rotation endpoint. To rotate a secret (or change the topic / callback URL), delete the subscription and create a new one (a fresh secret is minted on create).
- Re-registering the same
(topic, callbackUrl)pair returns400(duplicate) — delete the existing subscription first. - Failing subscriptions are not auto-disabled or deleted; they stay active and keep receiving future events. Each delivery retries 3× (60s → 300s → 900s); after that only that individual delivery is marked failed in the delivery logs.
- There is no test-delivery/ping endpoint on the app API — trigger a real event (e.g. place or cancel a test order) to exercise your handler.