We built Cartback on these APIs, and this is the map we wish we'd had on day one. It sticks to what Shopify documents, links the documentation for each piece, and flags the few places where our own testing adds something.
There is no "abandoned" event
The thing most people look for first doesn't exist: Shopify has no webhook that fires when a checkout becomes abandoned. The definition in the Abandoned Checkouts documentation is "a checkout is considered abandoned after the customer has added contact information, but before the customer has completed their purchase" — a state, not an event. In practice you subscribe to checkout events, watch for completion, and apply your own delay before treating a checkout as abandoned. (When the record first appears is covered in when does Shopify create an abandoned checkout.)
The webhooks
From the webhook topic list, these are the relevant topics:
| Topic | What it tells you |
|---|---|
checkouts/create | A checkout started. In our testing it fired within seconds of the shopper entering an email address. |
checkouts/update | The checkout changed — address, shipping, payment step. Also carries completed_at once the order is placed. |
checkouts/delete | The checkout was removed. |
orders/create | The reliable completion signal: match it to the checkout and stop any pending reminder. |
carts/create, carts/update | The pre-checkout cart. No contact information — useful for analytics, not for reaching anyone. |
The checkout payload carries what a recovery flow needs: an identifier and token, the cart_token that links it to the earlier cart, the shopper's email (once entered), total_price and currency, the line_items, completed_at, and abandoned_checkout_url — the link that returns the shopper to their checkout with everything still in it. It also carries buyer_accepts_marketing and a customer object with email_marketing_consent; treat those with care, because Shopify only records the checkout marketing checkbox at Pay now (details in this article).
Subscriptions are declared in the app configuration. This is the shape Cartback uses:
[[webhooks.subscriptions]] uri = "/webhooks/checkouts/update" topics = [ "checkouts/create", "checkouts/update" ] [[webhooks.subscriptions]] uri = "/webhooks/orders/create" topics = [ "orders/create" ]
Two things we learned the hard way: webhooks can arrive more than once and out of order, so the handler has to be idempotent and should prefer completed_at over arrival order; and a handler must answer quickly with a 2xx, or Shopify retries and eventually drops the subscription — do the work after you've acknowledged.
The GraphQL Admin API
The abandonedCheckouts query lists abandoned checkouts, with fields such as the recovery URL and line items; the documentation states it requires the read_orders access scope. It's the right tool for backfilling when an app is installed and for reconciling your own records — not for timeliness. In our testing the listing lagged the webhook stream by hours, while the webhooks were immediate.
The REST resource (legacy)
GET /admin/api/{version}/checkouts.json — the Abandoned checkouts resource — still works, but Shopify marks the REST Admin API as legacy as of October 1, 2024 and requires new public apps to use GraphQL exclusively from April 1, 2025. New work should start on GraphQL.
Protected customer data
Abandoned checkouts are full of personal data, and Shopify treats it as protected customer data. Beyond the base approval, the shopper's name, address, phone and email are protected fields that have to be requested individually and come with data-protection requirements (encryption, retention limits, and a review before the App Store listing is approved). The practical consequence: if a feature only needs counts, totals or line items, don't request the fields — the approval is lighter and there's less to protect. An app that actually contacts the shopper can't avoid it; Cartback stores email addresses encrypted at rest for exactly this reason.
Retention and the compliance webhooks
Shopify deletes abandoned checkouts older than three months from the admin, so your own copy needs a retention rule rather than growing forever. Every app that touches customer data also has to answer the three mandatory compliance topics — customers/data_request, customers/redact and shop/redact — and the App Store checks for them.
Putting it together
- Subscribe to
checkouts/create,checkouts/updateandorders/create. - Upsert the checkout by token on each event; mark it complete on
completed_ator a matching order. - After your chosen delay, if it's still open, send the reminder with
abandoned_checkout_url. - Backfill and reconcile with the
abandonedCheckoutsquery; expire your records on your own schedule.
The delay in step 3 is the interesting design decision — it's the one thing Shopify leaves entirely to you. We've written about it in when is the best time to send an abandoned cart email.