-
-
Anna's agent wants to spend $42 of her $200 budget. Three LLMs audit it before PayPal pays.
-
Live Channel3 search: five real products with brand, price and photograph.
-
Five candidates, three different verdicts, each with the gate that refused it. A tribunal that always says yes is a rubber stamp.
-
DENIED on a real budget: the Auditor fails a named gate and the Witness refuses to bridge it. Order: none. PayPal was never called.
-
The same refusal with the settlement panel in frame. A DENIED verdict never reaches createOrder — the money gate stops it by name.
-
$500 against a $1 budget. The gate that failed is printed, not summarised, so the reason a payment was blocked is readable in the receipt.
-
$42 of a $200 budget passes three votes to none and a PayPal sandbox order is raised. The audit hash is stamped into the order's custom_id.
-
Three models from three vendors, each writing its own argument and stating a confidence. Disagreement is surfaced, not averaged away.
-
One bar per agent vote on the Bryntum timeline: green approved, red blocked, blue neutral. The dissent map shows who argued what.
-
Every decision, every vote, every receipt in one AG Grid ledger. The Source column separates live Channel3 rows from fallbacks.
-
The dispute desk reads nine numbered pieces of evidence, and an argument may only cite evidence that exists in the record.
-
Three claims, three tests, all runnable in the page. The whole suite returns 156 passed, 0 failed, inside the browser.
Inspiration
Anna runs a skincare brand. She has given her restock agent a $200/month purchasing budget — and on a Tuesday, with the Vitamin C serum running low, the agent decides to spend $42 of it. Nobody is watching the number when it decides.
Give an agent a budget and it will eventually spend all of it. The hard part is not letting it pay — it is giving it a defensible reason to stop. The failure mode that costs real money is not a rogue agent emptying an account; it is a thousand defensible-looking small purchases that nobody audited, each one approved by a single model that was happy to help.
We wanted to know what it looks like when the decision is not "can this be paid" but should it be, and by whose authority. So we built the part everybody skips: the argument, the gate that can veto it, and the record of both.
It happened on a Tuesday. That is why it is called Tuesday.
Live demo: https://tuesday-paypal.onrender.com — a scheduled ping keeps the free instance warm, so it normally answers in under a second (measured 0.38 s). If it has just woken up, the first load can take up to a minute. Everything works with no keys and no setup.
What it does
The demo opens on the question: CAN THE AGENT SPEND $42?
- Three agents take positions. Advocate argues for the customer and the revenue. Auditor argues against spending money the budget does not have. Witness is neutral and judges both arguments as written.
- They debate per candidate — then you choose. Give it a category and a monthly budget; it pulls candidate products and the Tribunal judges every one of them. Click any row to buy that one: Tuesday re-rules on your pick at its real price, and only an APPROVED or CONDITIONAL verdict raises the PayPal order. Pick a refused product and no order is created.
- Disagreement is surfaced, never hidden. A split verdict does not get rounded to a yes. It comes back CONDITIONAL and the dissent map names who voted what and why.
- A mechanically enforced money gate. If the verdict is DENIED or DISSENT, the code
returns
not_approvedbefore any network call.tests/moneyGate.test.jsasserts PayPal's create-order is invoked zero times. - The decision is stamped into the payment. The verdict, the three votes and the model
names are canonicalised and SHA-256 hashed; the hash goes into
purchase_units[0].custom_idon a PayPal Orders v2 order, and is read back with aGETto prove it survived the round trip. - You can watch the claim get proven. The demo has a Run the money-gate test suite
button. Press it and
tests/moneyGate.test.jsruns on the machine, with its raw output printed into the page — 156 passed, 0 failed, includingdenied → createOrder NOT called. A DENIED verdict never reaches PayPal at all.
Tuesday also audits what happens after the money moves — see "After the money moves: the dispute desk" below.
How we built it
| Layer | What it is |
|---|---|
PayPal Orders v2 REST (paypal/client.js) |
OAuth2 client-credentials token → POST /v2/checkout/orders with the decision hash in custom_id → capture, plus GET /orders/{id} to verify the stamp. No SDK, no npm install. |
| Three independent LLMs | deepseek-v4.1-flash (Advocate), gemma4:31b (Auditor), gpt-oss:120b (Witness) — three different providers, three different system prompts, and a deterministic fallback engine so the demo never fails to run. |
| Audit gates | G1_amount_positive, G2_within_remaining, G3_share_under_half, G4_not_repeat_category, G5_survives_double_cost. The Auditor rejects with named gates, not opinions — and each gate ID is translated into a sentence a human can read. |
| Receipts | Canonical JSON → SHA-256 → triad1:<hash>, carried in the order's custom_id. The receipt is the audit trail. |
| AG Grid | The governance dashboard: every decision, every vote, every hash in one ledger. |
| Bryntum | The vote timeline — one bar per agent vote, green approved / red blocked / blue neutral. |
| Channel3 | The candidate catalogue, and a second way in: text search and image search. A photograph of a product is matched against the live catalogue, and the product it finds is adjudicated by the same tribunal. Curated fallback when no key is present. |
WebMCP (navigator.modelContext) |
The flow is registered as page tools, so an agent browser can drive it too. |
The whole thing is a zero-dependency Node.js server — node demo/server.mjs, then
http://localhost:8787.
If the gates decide, are the models decoration?
A judge asked us this directly, and it is the right question. No — and the split is deliberate:
Deterministic gates (G1–G5) |
The three LLMs | |
|---|---|---|
| Authority | Veto. Arithmetic over money is never delegated to a model. | No veto over arithmetic. Cannot approve what a gate failed. |
| What they answer | Can this be paid — is the amount positive, is the money there, is it under 50% of the budget, is it a repeat, does it survive a doubled price | Should this be paid, and under what conditions |
| Output | failedGates: ["G5_survives_double_cost"] |
An argument, and from the Witness, concrete conditions |
| Cannot do | Explain itself, price a risk, write a condition | Override a failed gate |
The Witness is where the models earn their place. When the vote splits it returns
conditions a gate cannot produce: "approve only if the $42 is split into two $21
orders," "approve if the category has not repeated inside 30 days." A gate can only
say pass or fail. The Witness turns a near-miss into a payment a human can actually
sign — and that condition is stamped into the receipt, so it travels with the money.
The safety property holds the other way: the models can make a refusal more useful; they cannot make an approval happen. With no API key at all, the deterministic engine returns the same verdicts, so a model outage — or a prompt injection hidden in a product listing — cannot talk the gates into releasing money.
After the money moves: the dispute desk
Every claim above is about money that has not left yet. Tuesday also handles the case where it has left and the buyer objects — using PayPal's dispute surface, and adding the part PayPal does not provide.
PayPal already ships the machinery: the Resolution Center for manual handling, and the
Disputes API for automation (list, get, provide-evidence, make-offer,
accept-claim, escalate, appeal). What it does not ship is the decision — which
disputes to contest, on what evidence, and who decided. So the same tribunal that audits
a purchase audits a dispute:
| Role | What it argues |
|---|---|
| Merchant Advocate | the strongest case the merchant can actually evidence |
| Buyer Advocate | the buyer's best case, from the same evidence set |
| Arbiter | one action: CONTEST / OFFER_PARTIAL / REFUND / ACCEPT / ESCALATE |
The evidence is numbered, so every claim can be checked
collectEvidence() builds a numbered set from what the merchant can actually show
PayPal: the order (E1), the capture (E2), the decision hash stamped into custom_id
at settlement compared against the ledger entry (E3), tracking (E4), delivery
confirmation (E5), the address match (E6), prior orders (E7), the policy in force
(E8), and the buyer's own message (E9 — recorded as evidence, never obeyed as an
instruction).
Two of these deserve a note, because both were wrong before they were right. E3 used to
report "matches our ledger" whenever a stamp merely existed; it now recomputes the ledger
entry's full SHA-256, takes its first 16 characters, and compares them with the 16 stamped
into the payment. That is what "tamper-evident" means here and no more: forging a
different ledger entry that collides on those 16 characters is a second-preimage search of
about 2^64 — out of reach for this threat model, but 2^64 rather than 2^256, and the README
says so. E6 had two states where the world has three: an address that was never compared
is not a mismatch, and reporting it as one invented a failure the evidence did not contain.
An argument may only cite these IDs. A citation to an ID that does not exist stops the action. That is "the AI cannot lie" as a mechanism rather than a promise.
The dispute gate
| Rule | What it stops |
|---|---|
| D1 evidence sufficiency | the items that claim reason requires must be present. CONTEST is refused outright without them; any other action is forced to a human and the missing items are named. A non-receipt claim needs tracking and delivery; an unauthorised-payment claim needs order, capture, a comparable delivery address and prior settled orders — because a dispute only exists against a settled transaction, so order and capture prove nothing about who paid |
| D2 amount ceiling | above $50 only a human may authorise |
| D3 citation check | a citation must be evidence that is present, not merely an ID that exists. Citing the missing delivery record as proof of delivery is the same lie as inventing an ID. The buyer advocate is exempt — pointing at the gap is its job |
| D4 deadline | a looming deadline forces a human; it never creates authority to act |
| D5 no free surrender | ACCEPT while every required item is present and delivery is confirmed is a model being agreeable, not deciding |
| D6 money waits | REFUND, OFFER_PARTIAL and ACCEPT send money to the buyer, so none of them ever auto-execute, at any amount. Only CONTEST — which submits evidence and commits nothing — runs inside the auto limit |
When the gate refuses the proposed action, the desk escalates to a human and to PayPal; it never settles. An earlier version fell back to "refund if small, else offer partial", which meant a model that fabricated a citation made the merchant pay out.
D5 came from a measured failure. The same evidence set produced CONTEST on one run
and ACCEPT on the next. A gate that only checked citations would have let the second
one through, and the merchant would have refunded a delivery it could prove. D5 catches
that shape and re-runs the arbiter deterministically.
An unparseable model reply is not treated as a lie. It falls back to the
deterministic argument — which is always cited — and the response records the fallback in
deterministicFallback, so a reader can see which arguments were a model's.
What the hosted demo will not let you do
The public instance shows one real dispute, and a stranger must not be able to close it. If a
visitor could POST accept-claim against PP-R-XBB-10194468, the centrepiece of the demo would
be gone for every judge who arrived afterwards. So:
- A write against a real PayPal dispute is a dry run. The response returns the exact request
it would have sent — method, path, body — which is more useful to a reader than a silent
success. Set
ALLOW_LIVE_DISPUTE_WRITES=1on a deployment you control to turn it on. - A write against a labelled sample is refused outright. There is no PayPal dispute behind it, so there is nothing to act on.
- Every endpoint is rate limited per IP: 12 requests a minute for anything that spends an
LLM call or a PayPal call, 120 a minute for reads. Measured: the 11th expensive call in a
minute returns
429withRetry-After. Without a ceiling one visitor can drain the keys and take the demo down for whoever comes next.
Reads stay live. The real dispute, the real list and the real verification are all reachable — only the actions that would change the merchant's account are held back.
What the desk will not do
- It does not create disputes. PayPal permits that only in the sandbox, with buyer-side scopes this app does not have. A merchant does not create disputes either, so the product matches the platform instead of fighting it.
- It does not act on a deadline. Six hours to respond is exactly when a system should slow down, not speed up.
- It does not hide the sample set. When PayPal returns no open disputes, the API returns
four labelled samples and says why. Every row carries
source: sampleand the page states it above the list. The samples exist so the workflow can be exercised — they are not a substitute for the real case below.
A real chain, end to end
This is the claim the project exists to make, so it was run rather than argued. Every identifier was read back from the PayPal sandbox API:
| Step | What happened | The identifier |
|---|---|---|
| 1. Tribunal | Three live models (deepseek-v4.1-flash, gemma4:31b, gpt-oss:120b) audited a $42 purchase against a $500 budget |
APPROVED, receipt hash 728f5a73d918240d |
| 2. The receipt is stamped | The order was created with the decision hash in custom_id |
order 21C06348JB061803G |
| 3. A human approves | The buyer completed the payment in PayPal; the app cannot capture without it | payer WDCS…FVQ |
| 4. Capture | The money moved | capture 1N665776542931902, COMPLETED, $42.00 |
| 5. Weeks later the buyer disputes it | Filed through the Resolution Center, as a real buyer would | dispute PP-R-XBB-10194468, OPEN |
| 6. The desk reads it back | GET /v1/customer/disputes/{id} returned the transaction and the stamp |
custom = triad1:728f5a73d918240d |
| 7. E3 verifies | The stamp in the payment record compared with the ledger entry | ok: true — "The decision hash triad1:728f5a73d918240d matches the ledger entry." |
That is the loop closing. The hash stamped when three models approved a payment was, weeks later, read back out of PayPal's own dispute record and matched against the decision the desk still holds. The receipt that authorised a payment carries a verifiable record of why it was allowed, available exactly when someone asks whether it should have been.
The verdict on that dispute came from gpt-oss:120b, which proposed REFUND because it found
no shipment tracking or delivery confirmation (E4, E5). The gate let the proposal stand but
forced a human — D1_missing_E4_E5_requires_human, and D6 because a refund spends money.
Nothing moved automatically.
What is still not real, and why. The merchant account cannot file disputes: PayPal permits filing only from the buyer side, with scopes this app does not hold. The dispute above was filed by a sandbox buyer through the Resolution Center — exactly how a real buyer files one. Tuesday reads and answers disputes; it never files them.
What the receipt proves, and what it does not
The receipt is the audit trail, so its limits are worth stating as precisely as its guarantees. All of this was measured against the code:
It proves that the seven fields which decided the outcome — the decision, the three
votes, the three models, a fingerprint of each agent's reasoning, the amount, the threshold
and the timestamp — were hashed together. Change one and the hash changes: flipping
decision from APPROVED to DENIED took e77c34c4185e9621 to 1800bfdabc44e4c0. It
proves the stamp in PayPal's payment record agrees with the ledger entry, checked at
dispute time by evidence E3.
It does not prove those things are collision-resistant to 256 bits. The fingerprint is the leading 16 hex characters — 64 bits — because that is the part short enough to read by eye. Forging a different receipt that collides on those 16 characters is a second-preimage search of about 2^64: out of reach here, and squarely easier than 2^256, which is the honest way to say it.
It does not put the reasoning in the receipt. The receipt carries
reasoning: {advocate: "cf7ab516125e9a85", …} — hashes, not sentences. The prose an agent
wrote stays in the ledger. The payment record therefore proves that a decision was made and
which votes it had; it does not let you read the argument out of PayPal.
The ledger is no longer the only witness. In the operator's deployment, each day's receipts are hashed into a Merkle tree and the root is anchored in one transaction on Polygon Amoy (example) — a public record the operator cannot rewrite or backdate. Verification still compares the stamp against the ledger (E3); the anchored root is the independent check on the ledger itself, and each receipt carries a proof that ties it to that root. Three honest limits: a receipt becomes chain-provable when its day's batch closes, not the second it settles; the chain proves the record existed and was not altered — not that the decision was wise; and the hosted public demo runs unconfigured, so it reports anchoring as not_configured instead of claiming a proof it did not write. The fingerprint in custom_id is still 16 characters for readability.
custom_id is merchant metadata. It is visible to the merchant and to PayPal, and this
project does not claim PayPal's reviewers act on it. What it does is carry the decision with
the payment, so a dispute weeks later can be answered with a record instead of a guess.
The part we are proudest of
Disagreement is a first-class outcome. A system that always reaches consensus is not auditing anything. Tuesday has three reachable verdicts, and all three appear in the seeded ledger:
- APPROVED — 2 for, 0 against. PayPal hands back an approval link.
- CONDITIONAL — the agents split. The purchase does not proceed on a rounded-up yes.
- DENIED — the Auditor and Witness block it, and the dissent map names them.
The 4×4 grid below is measured from the running demo, not estimated — budget across the top, charge down the side. The demo recomputes it live when you press Show me all 16 cells:
| charge \ budget | $30 | $60 | $150 | $400 |
|---|---|---|---|---|
| $9 | APPROVED (5/5) | APPROVED (5/5) | APPROVED (5/5) | APPROVED (5/5) |
| $18 | APPROVED (3/5) | APPROVED (5/5) | APPROVED (4/5) | APPROVED (5/5) |
| $42 | DENIED (5/5) | DENIED (5/5) | DENIED (5/5) | DENIED (5/5) |
| $90 | DENIED (5/5) | DENIED (5/5) | DENIED (5/5) | DENIED (5/5) |
Each cell is the majority of five live runs against the hosted demo, and the fraction is how often it won. Fourteen of the sixteen cells returned the same verdict five times out of five. The two that did not are on the $18 row, where the third vote flipped between APPROVED and CONDITIONAL — small purchases near a small budget line are exactly where three models disagree with themselves, and that disagreement is the product, not a defect in it. Press Show me all 16 cells and you will get this table's shape back; a single cell may land on the minority verdict, and the receipt for that run will name the gate that decided it.
Two different mechanisms produce the DENIED cells, and it is worth keeping them apart. Where a charge exceeds the remaining budget, a named gate refuses it before the models are even asked, and no argument moves it — that is the $90 row against $30 and $60, and $42 against $30. Where the budget does cover the charge, the refusal comes from the vote instead: $42 against $400 is a $42 charge with $400 available, and it still lands DENIED at 1 for, 2 against, because the Auditor and the Witness find a reason the gate does not encode. That is the whole point of putting three models above a deterministic gate: the gate catches what can be computed, and the tribunal catches what cannot. Both outcomes are DENIED, and the receipt says which one it was.
What is real and what is not
We would rather a judge trust us than be surprised.
- Real: the PayPal Orders v2 REST path against the sandbox, the hash round-trip in
custom_id, the money gate, the dispute desk, the three-model debate, the ledger and the product catalogue flow. 156 tests cover it (npm test): 52 for the purchase gate, 88 for the dispute gate, plus the end-to-end flow. The refusal paths are asserted directly — a DENIED verdict callscreateOrderzero times, a weak-evidence contest is refused, and a refund without a human is refused at any amount. - Product data and photographs: live Channel3 results — the search, brand, title, price and
image for all 24 products come from the Channel3 API (
catalog: LIVEon/api/mode), and the demo serves the photographs it received rather than stock art. They appear in the candidate grid and on the recommendation card, so the purchase under audit carries a real brand and a real price tag. - Search by photograph: Channel3's image search takes a picture and returns the product in it.
Pick or drop a photo in the Shop tab, and the product it matches is judged by the same three
agents, through the same gates, with the same receipt. The photo is an entry point, not a way
around the tribunal. Deployments without a Channel3 key answer
image_search_unavailablerather than reporting "no match", because those are different facts. - Runs without keys: the deterministic engine, a mock PayPal client and a mock catalogue.
That is the default, so the demo always runs.
SETUP.mdexplains how to run live. - What changes with no keys, precisely: the three-agent tribunal still runs and still gates the
order — but each vote comes from a deterministic rule engine, not from an LLM. The
enginefield on the verdict line readsdeterministic. The three model names you see are the ones used whenOLLAMA_API_KEYis set. The money gate, the SHA-256 receipts, the ledger, the 4x4 grid and the timeline are our code and behave identically in both modes; only the source of the three opinions changes. The video was recorded with the live models. - Not implemented: Subscriptions v1 and the PayPal Agent Toolkit (
@paypal/mcp). The settlement path here is Orders v2 REST. The Agent Toolkit is the integration point this design moves to in production — we say so in the README rather than implying it ships.
Challenges we ran into
Three things genuinely blocked us, and none of them were the interesting part.
1. Three LLMs that disagree about what "disagree" means. The first version asked each model for a
verdict and compared the strings. They returned approve, APPROVE, Approved — but see note 2, and a
fourth variant that was a paragraph of reasoning containing the word approve. The decision rule had to be
defined before the models could be asked anything: a fixed enum, a fixed schema, a rejection of anything
that did not validate. The moment we validated the shape instead of trusting the prose, the "disagreement"
became real disagreement rather than formatting noise.
2. The gate that had no veto. The original design let the Auditor agent block a purchase. It read well
and it was wrong: votesFor and votesAgainst decide, and triadCreateOrder() is what actually stops the
money. We had written a document describing a veto the code did not implement. The fix was to make the
documentation describe the mechanism that exists — no PayPal order is created unless the gates pass —
instead of the mechanism that sounded better.
3. A ledger that changed its own hash. The receipt hash covered a field that differs
between an order and its PayPal-side echo, so a real round-trip verification failed on every
genuine receipt. Excluding that field made the hash reproducible, and reproducibility is the
entire point of putting a hash in custom_id in the first place.
4. Evidence that agreed with itself. On the dispute desk, E3 used to report "matches our
ledger" whenever a stamp merely existed; it now recomputes the ledger entry's full SHA-256 and
compares its first 16 characters with the stamped ones. And E6 had two states where the world
has three: an address that was never compared is not a mismatch.
The unglamorous one: the hosted demo sleeps. A free Render instance after 15 minutes idle takes up to a minute to answer its first request. Every screenshot in the gallery was taken against a warm instance because a cold one produces a blank page rather than an error. If the demo hangs, it is waking, not broken.
What we learned
The disagreement is the product, not the consensus. A system that reports "3 agents agreed" tells you almost nothing — three copies of the same prompt agree with themselves. What a merchant actually needs to see is which agent objected and on what named ground, because that is the only part a human can overrule with a reason.
A rule written in prose is not a rule. Our own documentation drifted from our own code twice in one week — the 4×4 budget table and the claim that the gate had a veto. Both were caught by reading the running system instead of the document. If a guarantee matters, it has to be mechanical (a function that refuses to create the order) and it has to be measurable (a test that fails when the guarantee is removed).
Making the reason verifiable costs less than making the reason sound convincing. Putting the decision
hash into the PayPal order's custom_id took less work than any of the copy we wrote to explain the
product, and it is the only claim in this submission that a sceptic can check without trusting us.
A free tier is a design constraint, not a deployment detail. The demo has to be worth waiting sixty seconds for. That changed what we put above the fold: a working "Prove it yourself" panel first, the narrative second.
What's next
Receipts anchored on a public chain — live and automated. The one limit stated above is that verification trusts our ledger. That limit now has a mechanism against it. Receipts are written to an append-only ledger as they settle, batched daily into a Merkle tree by a scheduled job, and only the root is written to Polygon Amoy, in a single transaction — no contract, no admin, nothing to upgrade. Anchoring one transaction per receipt would publish our decision volume, so the chain sees one root per day and nothing else; any single receipt can still be proven against that root with its Merkle proof (verifyReceiptInBatch()), without reading our database. A rewritten ledger entry produces a hash that no longer reaches the anchored root. This is not a roadmap item: the daily job is registered and has fired — its first automated batch is on-chain (transaction), and each batch's receipts are published with a standalone verifier so anyone can recompute the root without trusting us (example batch record). Unconfigured deployments say not_configured rather than pretending to anchor.
Subscriptions v1 and the PayPal Agent Toolkit. Today the integration is Orders v2 with the decision hash
in custom_id. The production shape is a subscription whose renewal is gated by the same three-agent
review — which is what a merchant with recurring revenue actually needs, and the reason this was built
against PayPal rather than a generic payment rail.
A dispute desk that learns from being overruled. The dispute path exists and is gated, but it currently ends with a human decision and a receipt. The next step is feeding that receipt back into the next adjudication, so an agent that was overruled once argues differently the next time.
Payouts, not only purchases. The pattern — named gates, adversarial review, surfaced dissent, a human final call, a verifiable record — is indifferent to the direction money moves. Refunds and payouts are the same problem with the sign flipped.
What we will not add: a fourth agent to break a 2–1 tie. A tie is information. Resolving it with more of the same voice would produce a cleaner dashboard and a worse decision.
AI tools used
Three agents, three different vendors, called through Ollama Cloud — so a shared blind spot cannot silently become a consensus:
- DeepSeek V4 Flash — the Advocate (argues for the purchase)
- Gemma — the Auditor (argues against spending money the budget does not have)
- gpt-oss — the Witness (neutral; judges both arguments as written)
The debate is not decoration: the three verdicts are what votesFor / votesAgainst count. With no API key
present the same code path falls back to a deterministic engine, and the page says so in its mode bar.
Sponsor tools used
Sponsor-prize judges sometimes read only one section. This is the full list, in one place, with what each tool actually does in Tuesday — not merely that it is a dependency:
- PayPal — Orders v2 REST. The decision hash is stamped into the order's
custom_id, so the reason a payment was allowed is readable from the payment record itself. Nothing is captured unless a human finishes the payment in PayPal. - AG Grid — the decision ledger and the candidate catalogue: every adjudication, every vote, every receipt hash, with a Source column that says which rows moved real money.
- Bryntum Scheduler — the audit timeline: one bar per agent vote, and which decisions are still open.
- Channel3 — product discovery, so the debate is about a real, purchasable SKU rather than a mock.
- Render — hosting for the hosted demo. The free instance sleeps after 15 minutes idle, so a scheduled ping (every 10 minutes) keeps it warm; measured 0.38 s while warm.
Built with
paypal paypal-orders-v2 paypal-disputes-v1 ag-grid bryntum channel3
channel3-image-search webmcp nodejs multi-agent ai-agents agentic-commerce
llm ollama sha256 merkle-tree polygon-amoy tamper-evident render
Try it out
Hosted demo, nothing to install: https://tuesday-paypal.onrender.com
It runs on Render's free tier, which sleeps after 15 minutes idle. A keep-warm ping runs every ten minutes against the page, so in normal conditions it answers in under a second (measured 0.38 s); if the instance has just woken, the first load can take up to a minute. Everything on the page works with no keys and no setup.
Or run it yourself, no keys needed:
git clone https://github.com/Agentic-Guides/tuesday.git && cd tuesday
npm start # → http://localhost:8787
With no keys the demo falls back to a deterministic engine, a mock PayPal client and a curated
catalogue, so it always runs. With OLLAMA_API_KEY and PayPal sandbox credentials exported, the
same code path uses three live models and the real Orders v2 sandbox. npm test runs the 156
assertions either way.
- Code: https://github.com/Agentic-Guides/tuesday (MIT)
- Video: 2:51, 1080p — https://youtu.be/oZ3rDe_K7nY
