OVREL
02Art direction
Commerce
Systems
My own art-print label, with a store built around how people choose art for a wall and a service that holds every order for my approval.
- Role
- Founder. Art direction, the Shopify store and the order service, built with Claude Code.
- Status
- Live at ovrel.co since September 2026. My own venture, not client work.
- Stack
- Shopify, Liquid, Node.js, PostgreSQL, Inngest
Store
The store is arranged the way people choose art for a wall.
Most people know the room before they know the work. So ovrel.co adds four ways in beside the full catalogue of 285: a current edit of twelve, five rooms, curated pairs, and a product page that settles size and frame before checkout. The theme is custom Liquid. Shopify still runs the cart, checkout and payments.
Five rooms on the homepage, and Low / warm opened. A visitor who can’t name a style can still say what their room is like.
Two works chosen to hang together, added to the bag in one step.
Form at 24 × 36 in in natural oak, $179. Size, frame, price and one button. Point at it or press Scroll the site.




The same path on a phone. Screens captured from ovrel.co on 23 September 2026.
Collection
I tiered all 285 works myself.
Every work starts as a generated image. What makes it a label is the brief, the rejections and a tier on each piece: 37 lead, 137 keep, 47 maybe, 64 archive. A blind model review ranked 18 of my 23 lead works in its bottom two, so I kept its scores only to catch defects like garbled letters.
Campaign
The advertising carries five works at once.
A single work in a room sells that work, not the collection behind it. So the launch set is five works built to hang as one group: two typographic pieces, one numeral, one silhouette and one pattern. The room is generated. The edit is the work.
System
Nothing is sent to a supplier until I approve it.
Shopify runs the cart, checkout, payments, tax and fraud screening, and I did not rebuild any of it. Beside it runs a service of my own that decides which supplier prints each line, whether an order is safe to route, and what to do when a supplier stops answering. It runs in managed cloud, so the store keeps trading when my laptop is shut.
Production runs one step short of full automation. Every order is verified, recorded and planned, then held until I tag it in Shopify, from a phone if need be. Only then does it go to the supplier, under a key made from supplier, order and lines, so a retry can never place it twice.
- 17durable workflows
- 10of them on a schedule
- 18data models in Postgres
- 968tests, all passing on the deployed revision
- 01 · ShopifyOrderA webhook arrives.
- 02VerifySignature checked on the raw body. Forgeries are refused.
- 03RecordDeduplicated on the webhook’s own id, then stored.
- 04PlanA supplier per line. An unknown cost holds the line.
- 05 · the gateApproveHeld until I tag the order. Nothing is bought yet.
- 06SendOne supplier order per key.
A failing step retries with backoff, then stops as a dead letter with a task for me. A reconcile job every 15 minutes finds orders whose webhook never arrived.
Replay
Walk one invented order through the gate.
Start with the case that stops an order: a line with no supplier mapping. Then switch to a mapped line and take it all the way through. You do the person’s part; the right side is what the code writes.
The replay needs JavaScript. The order path above shows the same six stages.
The purchase-order key is real: a SHA-256 of supplier, order and line ids, hashed in your browser with the service’s own recipe.
Where each stage lives, and its test
| Stage | Source (src/) | Test today |
|---|---|---|
| 1 Verify | routes/shopify-webhook.ts | Passing |
| 2 Dedupe | routes/shopify-webhook.ts | Passing |
| 3 Plan | inngest/functions/orchestrator.ts | No dedicated test yet |
| 4 Hold | orchestrator.ts, step await-approval | No dedicated test yet |
| 5 Release | inngest/functions/approvals.ts | Only its schedule is tested |
| 6 Submit | supplier-orders.ts, lib/idempotency.ts | Key stability passing |
Checked against main on 25 September 2026. Stages 3 to 5 run in production without a test of their own. The full suite is 968 tests, all passing on the deployed revision (local run, 23 September 2026).
Four decisions
-
Rooms before genres
- Decision
- Five rooms, a twelve-work edit and curated pairs sit in front of the catalogue. All 285 stay one click away.
- Why
- Anyone can say what their room is like. A shopper who has to know a genre first is doing the curating.
-
My tiers, not the model’s ranking
- Decision
- I tiered all 285 myself. The model’s scores only catch defects.
- Why
- It put 18 of my 23 lead works in its bottom two tiers. It was measuring something, but not taste.
-
A duplicate delivery can’t become a duplicate order
- Decision
- Shopify delivers at least once, so verify the signature, dedupe on the webhook’s own id, and key every purchase order on supplier, order and lines.
- Why
- A retry does nothing twice, because keys come from business identity, not a timestamp.
-
Unknown is not zero
- Decision
- A missing cost is recorded as
UNKNOWNand holds the line. Spending stops at a cap and opens a task. - Why
- Holding an order costs a day. A wrong one costs the goods and the customer.
Current state
Live at ovrel.co since September 2026: 285 works from $39, printed to order in the United States. It is my own venture, and this page makes no claim about sales or conversion.
Who made what: the art direction, the edit, the storefront and the order service are mine. I specify, direct and review the work in Claude Code, then test and ship it. The images are made with generative and digital tools under my art direction, and every product page says so.
Longer read: How I made a webhook-driven workflow safe to retry.