Inspiration

AI agents can already browse, compare and check out. The missing piece isn't capability, it's trust. Nobody wants to hand an AI an open wallet: one bad prompt, one prompt injection hidden in a product title, and it buys a $300 gadget or something you'd never allow.

We wanted what a parent gives a kid with an allowance: freedom inside clear limits. So we built Leash: your AI shops, your rules hold.

What it does

You connect your PayPal wallet once, write your spending rules in plain English ("$150 a month, household, kitchen and books only, ask me for anything over $25, never alcohol"), and then tell the agent what you need, by typing or just by talking.

Every purchase the agent attempts goes through a deterministic rules engine with three possible outcomes:

  • ✅ Allow: small, in-policy items are paid instantly with your saved PayPal wallet.
  • ⏸ Ask: bigger items wait for your approval in the dashboard or on Telegram, with the product photo and Approve & pay / Deny buttons.
  • ⛔ Block: forbidden items are refused with a clear reason. Items on your never-buy list are stopped before the agent even searches.

There's also a kill switch, one-click refunds, a full activity log of every search, decision and payment, and history kept separately for each PayPal wallet you connect.

How we built it

  • PayPal
    • Vault (Payment Method Tokens v3) saves your wallet with your consent.
    • Orders v2 with vault_id lets the agent pay while you're not there.
    • A standard approve-and-capture order is the fallback.
    • Payments v2 refunds power the refund button.
    • Disconnecting deletes the payment token, which revokes the agent's access.
  • AI agent: Vercel AI SDK with Google Gemini and three tools: search_products, check_budget and request_purchase. It starts on Gemini 3.5 Flash-Lite for speed and falls back to larger models if one is overloaded, with per-step timeouts so it never hangs.
  • Channel3: real products from real retailers. Prices and categories come from Channel3, never from the model.
  • Rules engine: plain TypeScript with unit tests. The model can only pass a product id. Price and category are read from a server-side catalog cache, so a confused or hijacked agent can't misreport them.
  • Voice input: the browser records your request, converts it to 16 kHz WAV, and Gemini transcribes it. You review the words before sending, because a misheard word shouldn't spend money.
  • Telegram bot: approval requests come to your phone through a webhook verified with a secret header. Only your linked chat can approve.
  • Next.js 16 with a light and dark UI, deployed on Render with Render Postgres so wallets, rules and history survive restarts.

The decision rule

For a product with price $p$ and category $c$, with $S$ already spent this month, a monthly budget $B$, a per-item cap $M$ and an auto-approve limit $A$:

$$ \text{decision} = \begin{cases} \text{BLOCK} & \text{if } c \notin \text{allowed} \;\lor\; \text{blocked term} \;\lor\; p > M \;\lor\; S + p > B \;\lor\; \text{paused} \ \text{ASK} & \text{if } p > A \ \text{ALLOW} & \text{otherwise} \end{cases} $$

The AI never evaluates this. Code does. A human approval can turn ASK into ALLOW, but nothing can override a BLOCK, and budgets are checked again at payment time.

Challenges we faced

  • Keeping the AI out of the decision. The rules live in code, not in the prompt, so a prompt injection in a product title can't change the outcome.
  • Real catalogs are messy. Searching "oat milk" returned an oat-milk makeup foundation. We taught the agent to refuse look-alike products, and tightened category matching to whole words after "home & garden" decor slipped past a "home" rule.
  • PayPal sandbox country rules. Our Nigerian sandbox accounts couldn't save a wallet, so we set up US sandbox accounts to use Vault.
  • Model capacity spikes. One overloaded model once stalled the agent for over five minutes. We added model fallbacks and timeouts, so it now answers in seconds or fails cleanly.
  • Notifications on a free host. Our first push service rate-limited requests from Render's shared IPs, so we switched to a Telegram bot. It turned out better, because you can approve right from the notification.
  • Voice accuracy. Our first transcription prompt listed shopping words as hints, and the model started replying with only those words. Removing the hints and checking for silence fixed it.

What we learned

  • How PayPal Vault makes "payer not present" payments possible, and why that power needs a policy layer the agent can't talk its way around.
  • That agent safety is mostly architecture, not prompting: give the model as little authority as possible (a product id) and keep every fact that matters (price, category, budget) on the server.
  • That every guardrail needs a visible reason. Users trust a "blocked" result when it says why.

What's next

Family mode (a parent sets a teen's leash), weekly budgets per category, per-merchant rules, multi-user accounts, and approvals over WhatsApp.

Built With

  • channel3
  • google-gemini
  • next.js
  • node.js
  • paypal
  • paypal-orders-api
  • paypal-payments-api
  • paypal-vault
  • postgresql
  • react
  • render
  • render-postgres
  • telegram-bot-api
  • typescript
  • vercel-ai-sdk
  • vitest
  • web-audio-api
  • web-speech-api
Share this project:

Updates

Submission history