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
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.
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.
- The ERP…
- The bridge…
- 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.
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.
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.
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.
04
Stock
The ERP has no webhooks, so the bridge reads what changed since its last cursor and sets absolute quantities in the store.
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.


