Skip to main content

Webhook Topics

Webhook topics notify your app when events occur in the merchant’s store. Subscribe to a topic with POST /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.
The topics below are the ones you can subscribe to via 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_id falls back to product_id for products with no variants.
  • There is no digital flag on the order — drive digital/course fulfilment off line_items[].product_id.
  • The order body has no storeId; the sending store is the X-LMS-Shop-Domain request 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 checkout
  • null — 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/create
  • orders/updated + orders/cancelled; a POS refund emits refunds/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, scope read_orders) and reconcile against the authoritative order.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:
  • billingStatusactive | trial | past_due | cancelled | free.
  • status — the installation state: active (app functional) or disabled (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 restores billingStatus: active automatically.
  • reason — present on billing_grace_expired (the daily sweep disabled the install) and cancel_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 via POST /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.json create 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 returns 400 (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.