Case study · Shopify · Embedded admin app
A Shopify stocktake that can't overwrite a sale
Shopify retired its Stocky app on 31 August 2026. We built Tallyo for stocktakes without it: several people scan at once, every difference is reviewed before Shopify changes, and a whole count can be undone. It is our own product, built and shipped by our team, and it has been live on the Shopify App Store since 9 October 2026.
The problem
Counting stock in a shop that stays open
Stocky was Shopify's own inventory app. It left the App Store in February 2026 and stopped working for everyone on 31 August 2026. Shopify now points merchants to Stock counts in the admin and Quick count in Shopify POS.
Both have gaps that matter in a real stocktake. An approved stock count can't be undone. Quick count updates quantities as soon as you submit, with no review step. Counting on the shop floor in the POS app needs POS Pro, a paid add-on for each location. And Shopify's help pages don't say what happens to an item that sells between being counted and the count being approved.
That last point is the hard one. If the till sells two units while an aisle is being counted, writing the counted number straight over Shopify's quantity leaves the stock level wrong by two.
What we built
Count, review, apply, undo
Tallyo is an app embedded in the Shopify admin. A count covers one location: everything stocked there, or only products matching a vendor, product type, tag or collection. People count with any barcode scanner that types like a keyboard, with the camera in the Shopify mobile app, or by typing. Nothing in Shopify changes until someone reviews the differences and presses Apply.
Checked writes
Each update is sent with the quantity Tallyo expects Shopify to have. If the item sold or was received since the count began, Shopify refuses that one item and Tallyo marks it for a recount.
One-click rollback
Every adjustment is stored with the value it replaced, so a whole count can be put back. The rollback is checked the same way and leaves alone anything that has moved since.
Several counters, one count
Every scan is saved as its own entry, with a label such as "Aisle 3", and totals are worked out from those entries. Two people scanning the same shelf never overwrite each other.
Blind counts
Counters see what they have counted, not what Shopify expects. While a blind count is open, the server removes expected quantities and differences from every response, not just from the screen.
Engineering
The hard parts, and how we solved them
- A sale during the count. When a count starts, Tallyo records Shopify's on-hand quantity for every item. Each write carries that number through Shopify's compare-and-set, and Shopify rejects the item if the real quantity has changed. The item is marked stale for a recount instead of the app guessing.
- A write that dies halfway. Shopify can accept an update and the app can crash before it records what happened. Before each call, Tallyo saves the batch of up to 50 items with the idempotency key it will use. If a batch is left unfinished, the next apply or rollback sends the same key again, and Shopify returns the original result instead of writing twice. Shopify keeps that result for 24 hours, so a batch older than 12 hours is settled against the current quantities rather than re-sent.
- Two people pressing Apply. Apply and rollback take a lock on the count through a conditional database update, so two requests can't run at once. A lock left by a crashed request is taken over after 10 minutes, and lines already written to Shopify are frozen.
- A filter typo that counts everything. Shopify ignores a product search with an unknown field and returns every product, so a mistyped filter would quietly start a full-store count. Tallyo checks the filter's field names against the ones Shopify accepts before the count starts.
- Large catalogues and rate limits. The starting snapshot for a big location takes many pages of API calls. The app watches Shopify's remaining query budget and pauses before it runs out.
- Shopify's App Store rules. Tallyo asks for three permissions only: products, locations and inventory. It never reads orders or customers. The three mandatory compliance webhooks are in place, and uninstalling deletes every row for the store, access token included. Request logs record paths only, because Shopify's embedded requests carry a session token in the query string. The app runs inside the admin through App Bridge, uses Polaris web components, and adds nothing to the storefront.
- Billing through Shopify. Plans are set up in Shopify's managed pricing. The server reads the store's active subscription and enforces the plan when a count starts and again when it is applied. The free plan allows one location and counts of up to 100 items.
Result
Where it is now
Tallyo was submitted to Shopify on 26 September 2026, two days after the first commit, and passed Shopify's automated checks. It was approved and went live on the Shopify App Store on 9 October 2026. Since then we have shipped blind counts and a phone-first counting screen (10 October) and a free plan (13 October).
Before launch, the full loop ran on the production build in a development store: count, scan, review, apply, export to CSV and roll back. Camera scanning was tested on an Android phone in the Shopify mobile app. The committed code has 81 automated tests, including service tests that run apply and rollback against a throwaway database and a fake Shopify API.