Your app receives platform events at its webhook_endpoint_url, signed with your app’s client_secret. Delivery runs on the same signed, retrying pipeline as Agent Gateway webhooks — the difference is that app webhooks are keyed by your client secret, so you already hold everything needed to verify them.

Subscribing

You do not call a subscribe endpoint. Declare the topics you want on an app version’s webhook_topics, and set the delivery target as your app’s webhook_endpoint_url. When a merchant installs an approved version, the platform auto-registers those topics for that install and delivers to your endpoint.
To change your endpoint or topics, ship a new version — the change applies to installs of that version, including installs that move to it on approval because it asks for no new required scope.

Delivery format

Each event is a POST to your endpoint with a signature header and a JSON envelope:

Verify the signature

The signature is HMAC-SHA256(client_secret, raw_body), hex-encoded and prefixed sha256=. Compute it over the raw request bytes — not a re-serialized object — and compare in constant time.
Your endpoint must return 401 on an invalid signature and 200 on a valid one. This is probed during review: the platform sends a request with a deliberately wrong HMAC and expects a 401. An endpoint that accepts unsigned or mis-signed payloads fails review.
During the 72-hour secret-rotation grace, signatures may be produced with either the new or the previous secret. If you have just rotated, accept a body that verifies against either secret until you have fully cut over.

Retries

A delivery is considered successful on a 2xx. Any network error or a >= 400 response is retried — up to 5 attempts with exponential backoff. Make your handler idempotent: de-duplicate on webhook_id, since a retry re-delivers the same event.

App lifecycle topics

Commerce topics

App versions may also subscribe to store-resource topics to react to merchant activity. Common ones: See the webhook reference for the full topic catalog. Only topics your app actually needs should appear on a version — unused subscriptions are noise the review team will question.

Order payloads

orders/create is sent when checkout completes an order, on every payment method, to each app installed on that store that subscribes to the topic. Each entry in line_items carries the line’s line item properties as app_properties:
install_id is the install that received the delivery (the same id as app/uninstalled, the compliance topics and the session token’s sid), so an order from a store your app has not seen yet can still be attributed to the right install. app_properties is {} for a line without any. Entries arrive exactly as the cart API saved them, private _ keys included. Read the entry under your own app handle.

Compliance payloads

The GDPR compliance topics (customers/data_request, customers/redact, shop/redact) use the same envelope and signature as every other webhook. Public apps must declare all three. Every compliance payload names the install it is for: shop/redact is sent 48 hours after an uninstall, and is not sent when the app was reinstalled in that window. Delete everything you hold for the install:
customers/redact is sent when a merchant deletes a customer, to apps holding a customer or order scope (customers:*, orders:*, draft orders, returns or checkout). Delete that customer’s data and the listed orders’ personal data:
customers/data_request is sent when a merchant uses Request customer data on a customer’s page (a customer asked what personal data the store holds), to the same apps. Send the data you hold about that customer to the merchant (by email to the store’s contact address, or in your app’s UI). It carries the same fields as customers/redact, with orders_requested in place of orders_to_redact and data_request: { "id": "…" }:

Answering compliance webhooks

  • Verify the signature first. Answer 401 when it does not match (review sends a request signed with the wrong secret and expects 401).
  • Answer 2xx as soon as you have accepted the request, then do the work. Finish it within 30 days.
  • A topic can go to its own URL: set compliance_webhook_urls on the app (dtc app config). A topic without one goes to your webhook endpoint.
  • Failed deliveries are retried with backoff (1 minute, doubling, up to 6 hours between tries) for about three days. data_request.id and install_id stay the same on every retry, so use them to de-duplicate.