-
-
Criée clears standing wishes against what small sellers have left at closing, and PayPal settles every match.
-
The problem: unsold bread at closing, and buyers who are not there when the markdown comes.
-
The board: every lot clearing tonight in Strasbourg, by falling clock, closing auction or group order.
-
A wish, read back from the exact terms about to be signed, then signed with a passkey. PayPal is saved once in the Vault.
-
The seller photographs the shelf. Gemini lists items, counts and prices; a fact like gluten-free must point to its label.
-
The falling clock steps the price down until every loaf is sold; at the counter, the pickup code captures the PayPal hold.
-
The buyer's pickup code. A no-show releases the hold and the loaf goes back on the clock.
-
Paid within the minute through PayPal Payouts, and the seller's assistant invoicing a café with the PayPal Agent Toolkit.
-
The anonymised order book and the demand curve in AG Charts: how many units the standing wishes would buy at each price.
-
Every step is an event in a signed, hash-chained ledger, verified in one click and used as dispute evidence.
-
One sale end to end: Criée's steps above, the PayPal calls below, with the times measured on the sandbox.
-
A group order's 40 PayPal holds, taken concurrently: all held 2.6 s after the burst started, against 91 s one after another.
-
Simulated loaves sold and wasted per bakery-evening: a fixed markdown, a surprise bag, and Criée's falling clock.
-
Architecture: Gemini reads words and shelves, a deterministic engine clears, and PayPal holds, captures, pays and answers disputes.
-
Results: latencies measured on the PayPal sandbox, and a simulation of 12 bakeries and 10,000 residents in Strasbourg.
About the project
Inspiration
Every evening, independent bakers in Strasbourg throw away bread they could not sell. The people who would gladly buy it are at work, at home, or simply not looking at that moment. A markdown at 18:30 only reaches whoever happens to walk past. An app only reaches whoever opens it.
Agent payments change that. If my own agent can hold a signed instruction such as "an organic sourdough loaf, twice a week, under €5, after six", then I am present at every closing without being there. Read that way, an intent mandate is a limit order, and a city's leftovers are a market waiting to be cleared. I audit payment systems at a bank, so I built the money side the way I would want to audit it: every hold, capture and payout tied to a signed event, and every dispute answered from that record.
What it does
Criée is a clearing house for what small food sellers have left at closing.
- A wish, signed once. I describe what I want in my own words, in French or English. Criée reads it back as a sentence generated from the exact terms I am about to sign. I sign the SHA-256 of those terms with a passkey and save PayPal once through the Vault. My AI agent can draft wishes for me over MCP, but only I can sign them.
- A shelf, read by its labels. At closing, the baker photographs the shelf and can add a voice note. Gemini lists each item with its count and price. A fact such as gluten-free is kept only when it points to the label crop it was read from, or to the baker's own words. The baker confirms each lot and picks a clearing mode.
- A market that clears. A falling clock steps the price down until every unit is sold. A closing auction gives everyone one fair price. A group order goes ahead only if enough people commit.
- PayPal settles each match. When a match clears, Criée authorizes the buyer's saved wallet with nobody at the screen. At the counter, the six-digit pickup code captures the hold and records the pickup on the PayPal order as tracking. The baker is paid through PayPal Payouts within the minute, minus a 4% fee. A no-show releases the hold, and the loaf goes back on the clock.
- Forty holds at once. When a farm's group order reaches 40 boxes, Criée takes all 40 PayPal holds in one burst, in 2.7 seconds on the hosted demo, and captures them. Below the target, nobody pays.
- Disputes answered from the ledger. Every step is an event in a signed, hash-chained ledger. When a buyer tells PayPal "I never ordered this", Criée sends one evidence pack through the Disputes API: the signed wish, the clearing round, the pickup scan and PayPal's own tracking record.
- A console and an assistant for the seller. An AG Grid console lists every sale with its PayPal authorization and capture, refunds, payouts, disputes and the anonymised demand around the shop. The seller's assistant uses the PayPal Agent Toolkit to invoice a café and show the payment QR code. Refunds wait for the seller's tap.
How I built it
The engine decides, the model proposes. The clearing engine is a pure fold over ledger events. It reads only enums, integers, coordinates, timestamps and identifiers, and a test fails if any free-text field could reach it. Property tests generate random markets with revocations, refused holds, pickups and scheduler wake-ups, and check that units never oversell, that every match lies inside its signed wish, that every hold ends captured or voided, and that replaying the ledger reproduces every match. Each lot commits to a hashed seed when listed and reveals it when it closes, so every lottery can be replayed.
PayPal, end to end, with Criée as merchant of record. Vault v3 setup and payment tokens; Orders v2 AUTHORIZE with vault_id and a merchant-initiated stored credential; Payments v2 capture, void and partial refund; order tracking with carrier OTHER ("Collected in store"); Payouts with batch status; Customer Disputes with provide-evidence; webhooks with signature verification and de-duplication; the PayPal Agent Toolkit for disputes and invoices. Every mutating call carries a PayPal-Request-Id derived from the match, so a retry never takes a second payment. Before writing the product, I probed every one of these calls against the sandbox and chose the settlement design from the results.

One sale, end to end, as recorded on the ledger. Criée's steps on top, the PayPal calls below, with the times measured on the sandbox.

The 40 PayPal holds of one group order, taken concurrently on the sandbox. All 40 were held 2.6 s after the burst started. The same calls made one after another add up to 91 s.
Gemini 3.7 Flash on Vertex AI, inside guardrails. Structured output for wishes, vision with box_2d boxes and audio for shelves, number-checked sentences for demand advice, and function calling with thought signatures for the assistant. Live calls stop past a daily cap, a per-visitor limit and a fixed end date. The demo inputs are recorded, and every component has a deterministic fallback, so the product runs with no key at all.
Stack. Python 3.12, FastAPI, Pydantic and SQLite with an Ed25519-signed hash chain; React, TypeScript and Vite with AG Grid and AG Charts; WebAuthn passkeys; the MCP Python SDK; ReportLab for evidence PDFs. The hosted demo runs on AWS EC2 (Graviton) behind a Cloudflare tunnel. 58 tests, including the Hypothesis invariants.
Impact
I simulated a week of 12 bakeries and 10,000 residents in Strasbourg, 20 seeds, with Criée running through the real engine. With 3% of residents holding a bread wish, Criée cut leftover bread from 6.81 to 3.96 loaves per bakery-evening against a fixed markdown, and raised what each baker keeps from €15.27 to €26.75 (simulated). When every resident is assumed to see every markdown, Criée still wastes less (4.06 against 4.42 loaves) and earns more (€27.05 against €20.86), because the clock sells each loaf near its buyer's limit. Every assumption is listed in the repository.

Simulated loaves sold and wasted per bakery-evening: a fixed markdown at 18:30 (A), a surprise bag (B) and Criée's falling clock (C). 12 bakeries, 10,000 residents, 20 seeds. Whiskers are 95% intervals of the mean.
Challenges I ran into
Charging a buyer who is not at the screen needs the right PayPal flow: a vaulted wallet, an authorization flagged as merchant-initiated and unscheduled, and an idempotency key per match. A group order needs every hold taken at the same moment, so the settlement worker fires them concurrently and records the burst time on the ledger. A model must never decide who pays what, so the engine's inputs are typed down to enums and integers, and the sentence a person signs is generated from those types rather than from model text.
Accomplishments that I'm proud of
- One wish from a judge completes the farm's group order, and 40 real sandbox holds are taken and captured in under three seconds.
- A dispute raised on PayPal's own site is answered automatically from the signed ledger, with PayPal's tracking record of the pickup in the evidence.
- The same product runs with live keys, with no keys, and after the model budget ends, because every AI step has a recorded and a deterministic path.
What I learned
Standing wishes turn presence into a property of the buyer rather than of the moment. Once the wish is signed and the wallet is saved, PayPal's authorization and capture split fits a counter pickup exactly: the hold proves intent, the scan proves delivery.
What's next for Criée
Pilots with two bakeries and a farm near Strasbourg, multiparty settlement so sellers are paid directly as PayPal payees, and wishes that agents from other platforms can hold through the same MCP tools.

Log in or sign up for Devpost to join the conversation.