Skip to content
← All work

Case study · 2026 · Architect

Relay

Every return is a decision. Relay makes the best one, and can prove what happened to the item.

Relay is an AI-powered second-life commerce platform. A Go decision engine routes each returned product across seven disposition channels, weighing value, carbon, demand and chain-of-custody; a multimodal model writes a “Condition Passport” for the item; and a hash of that passport is anchored on Polygon.

Disposition channels
7
Services
API · decision engine · ML
Matching
Retrieve → rerank, pgvector HNSW
Chain of custody
SHA-256 passport hashes on Polygon

The problem

A returned item usually takes the most expensive path by default: back to a warehouse, then maybe a discount. The better answer depends on the item’s condition, who nearby wants it, and what the route costs in carbon.

That’s a decision with several competing objectives, made thousands of times, which is exactly the shape of problem that belongs in a small, fast, testable engine.

How it’s built

Three services behind HTTP contracts defined before any logic was written.

scoreroutematchReturn requestcheckout · apprelay-apiFastAPI · Celeryrelay-engineGo · scoring · pricing7 channelsdispositionBedrockCondition Passportrelay-mlgrading · embeddingsPostgrespgvector · HNSWPassport hashanchored on Polygon

Scroll the diagram sideways →

Hot math in Go, orchestration in Python
Disposition scoring, rescue pricing and matching run in a stateless Go engine. The FastAPI backend owns returns, persistence and credits, and calls the engine over HTTP.
Retrieve, then rerank
Titan embeddings and pgvector HNSW fetch candidates; a Bedrock reranker and category and size gates order them.
Rescue with decay pricing
Hyperlocal “rescue” offers get a price that decays with time and distance, so an item finds a nearby buyer before it becomes a shipping cost.
Anchor the hash, not the data
Only a SHA-256 hash of the passport goes on-chain. The chain proves the record wasn’t altered without publishing the record.

Decisions that cost something

01

Contracts first, UI last.

The build order was schema, then endpoint design, then flow, then logic, and the interface last. Three services only stay honest if their contracts are written down before any of them is.

The costNothing demoable for a while. Nothing rewritten later.

02

Multi-objective, not a single score.

Value, carbon, demand and chain depth are separate inputs with guardrails, so the engine can explain why it chose a channel, not just which.

The costMore parameters to tune and defend.

03

Prevent the return where you can.

Checkout-time return prevention and “reverse wishlists” (the Genie) match supply to stated demand before an item ever ships back.

The costA feature that makes the platform’s own volume smaller, deliberately.

What went wrong, and what it taught me

A system with seven outcomes.

The hardest part wasn’t any single model; it was keeping seven different outcomes comparable. Resale, rescue, donation and the rest each measure “good” differently, so the engine normalises them before it ranks them, and refuses to route anywhere that would burn more carbon than it saves.

Stack & links

GoFastAPICeleryPostgres + pgvectorAmazon BedrockRedisReactPolygon