Work / Handoff

Side project · live demo · October 2026

Orders that reach the ERP once, even when the ERP is failing.

Handoff connects a real Shopify store to an ERP. Every order lands exactly once; when the ERP is down, slow or refuses an order, the bridge retries, waits or holds it for replay, and a nightly check catches webhooks that never arrived. I built it from the problems I meet in real integrations, and you can break the ERP yourself.

Open the live console
Role
Design, backend, frontend, ops
Stack
TypeScript · Next.js · PostgreSQL · QStash · Shopify Admin API
Status
Live demo
Links
Console at handoff.danielgcravo.com · source code private for now

How to try it

  1. 1Switch a fault on under Break the ERP, for example Down.
  2. 2Place a test order and click it to follow its trail.
  3. 3Watch the bridge retry, waiting longer each time, then switch the fault off.
  4. 4Try Rejects data: the order goes to the failed queue until you replay it.

The video is built from the console's real screens. Kilnhouse Coffee is a fictional roaster; the store is a real Shopify development store.

The console

One screen: every order, the trail of each try, and the switches that break the ERP.

Anyone can switch an ERP fault on, place a real test order and watch what the bridge does. The screens below are from the live demo.

Handoff console with the Down fault on: order #1018 has failed three times with a 503, each wait longer than the last
Down is on · #1018 failed three times with a 503, waiting 10, 20 and then 40 seconds
Handoff console with Rejects data on: order #1019 was refused with a 422 and sits in the failed queue with a Replay button
Rejects data is on · #1019 was refused, so it went straight to the failed queue, no retries
Handoff console after the replay: order #1019 was sent back and created in the ERP as SO-10009
Replay · once the cause is fixed, #1019 goes back and lands in the ERP as SO-10009

What it handles

Every way the ERP can fail, and what the bridge does about it.

On the store's side: a webhook delivered twice is handled once, two webhooks for one order make one order, and one that never arrives is found by the nightly reconciliation.

is down (503)
retries six times, 10 s to 5 min apart, then hands the order to the failed queue
is slow
gives up after 8 s; the next try first asks the ERP whether the order arrived anyway
rate limits (429)
waits as long as the ERP asked before trying again
saves the order and loses the answer
finds the order on the next try instead of creating it twice
refuses the order (422)
sends it to the failed queue at once, because retrying cannot help
changes stock on its own
pushes the new quantity to the store, minus orders still on their way

How it works

Five small jobs, each safe to run twice.

  1. 01

    Webhook

    Checks Shopify's signature against the raw body, stores the order and answers at once, so a slow ERP never looks like a failed webhook.

  2. 02

    Queue

    QStash calls the delivery job until it answers. The bridge, not the queue, decides when to stop: 503 asks for another try, 200 means delivered or set aside.

  3. 03

    Delivery

    Claims the order so two tries never run together, asks the ERP whether it already has it, creates it if not, and records every try.

  4. 04

    Stock

    The ERP has no webhooks, so the bridge reads what changed since its last cursor and sets absolute quantities in the store.

  5. 05

    Reconciliation

    Every night: store orders the bridge never received are taken in, and stock that drifted is corrected.

Decisions

The choices that keep orders from being lost or doubled.

Idempotency
Three levels: the webhook ID stops redeliveries, the order ID stops two webhooks about one order, and a lookup in the ERP before every create stops the duplicate a lost answer would cause.
Errors
No answer, timeouts, rate limits and server errors are transient and retried. Any other refusal, or a product with no mapping, is permanent and waits for a person.
Stock
Absolute quantities only, so sending the same number twice changes nothing. Units already sold but not yet in the ERP are held back, so the store never sells them twice.
Privacy
Test orders carry no customer, every order reaches the ERP under one account, and the demo's limits store a salted hash of the visitor's address, never the address.
Tests
62 tests, including integration tests that switch on each ERP fault and check that no order is lost or duplicated.

Your systems

Need your store, ERP or CRM to stay in step, even when one of them fails?

I build integrations with retries, a failed queue and a nightly check, documented, tested and handed over in your name.

Tell me about your project
Back to work