Inspiration
AI agents can now propose purchases, but a proposal is not permission. A language model can misread an instruction, overshoot a limit, or be steered by text on a page, and a payment rail does not know what the person actually allowed. Ostensia puts a deterministic, item-level check between the agent's proposal and the payment: the person confirms the terms, and code (not a model) decides whether a cart fits them.
What it does
Ostensia lets a person set exactly what an AI shopping agent may buy, as a mandate: which products, which variants, how many, at what unit price, a maximum total, and an expiry. When a cart is proposed, deterministic server-side code checks it against the confirmed mandate. A cart that stays within the terms can be authorized and captured through PayPal Sandbox. A cart that breaks them cannot be captured; if funds are already on hold, the authorization is voided. The person reads the decision in plain words, plus an audit record of every step.
What is actually functional
- Mandate creation and confirmation, with validation on the server.
- The deterministic pre-check and final check, with plain-language explanations.
- A real PayPal Sandbox lifecycle: authorization, capture, and void, driven by those checks, with the mandate reference read back from PayPal.
- Revoking a mandate and a settle that then blocks the cart and voids the authorization.
- The append-only audit log, the audit record page, and the list of attempts.
- Idempotent purchase attempts and handling of replies that do not come back.
- AI drafting and the AI shopping agent, when an Anthropic key is configured.
- An AI-agent cart through the payment path: In one developer-reported sandbox run, an AI-agent cart passed both checks and was authorized and captured. The agent is a non-deterministic model, and this was a single run, so it is not a guarantee, and it is not shown in the video.
What is simulated
- The shop: a synthetic catalogue and six scenarios. Ostensia acts as the merchant. Nothing is a real merchant.
- The "simulated shopping agent" scenarios used to demonstrate the gate. They are deterministic and labelled.
- All money is PayPal Sandbox money. It is not production software.
How I built it
The flow
- Mandate. The person writes terms by hand or asks the AI to draft them. The AI's draft is shown as a draft; Ostensia runs its own completeness check; the person reviews, edits, and confirms. Ostensia stores a SHA-256 reference with the confirmed mandate so that later records can be traced back to those exact terms. It is application-level traceability, not proof of who approved it.
- Proposal. A cart comes from the simulated shop (six scenarios, including over-limit ones) or from an AI shopping agent. Both are labelled by source.
- Pre-check. Before anything is sent to PayPal, the cart is checked for product, variant, quantity, unit price, total, currency, expiry and revocation. A block is explained in plain words, and nothing is sent to PayPal.
- Authorize. If the pre-check passes, PayPal Sandbox puts the funds on hold. No money moves.
- Settle. Ostensia reads the authorization back from PayPal, runs a final check against the current state of the mandate (for example, whether it has been revoked), and then captures or voids. At most one capture or void is sent per attempt.
- Audit record. Every decision and every PayPal request is written to an append-only log, with each PayPal write recorded before it is sent. The state is rebuilt from that log-on start. A page shows the record and lists all attempts.
PayPal integration. Ostensia calls the PayPal Sandbox REST API (the host is fixed to the sandbox in code).
- The sandbox buyer approves a saved payment method once (a vault id). Each attempt creates an order with the
AUTHORIZEintent against that saved method, and Ostensia reads the order and authorization state from PayPal rather than assuming it. - The mandate's reference is sent in the order's
custom_idand read back from the authorization. - Settling captures or voids the authorization. A void is used when the final check blocks the cart, for example, after the mandate was revoked. Held funds are released on PayPal's schedule, not ours.
- Requests carry a stable
PayPal-Request-Idper attempt and operation, and the attempt id is chosen by the caller, so sending the same request again returns the same attempt, not a second one. - In the demonstrated flow Ostensia acts as the merchant, with a simulated shop. This is not a way for an agent to spend a buyer's PayPal wallet at arbitrary merchants.
AI integration. Two optional features use Anthropic's API (the model is claude-opus-5-5).
- Mandate drafting: the AI proposes structured terms from a description. The draft never fills the form until the person accepts it, and Ostensia's own completeness check runs separately.
- AI shopping agent: given a mandate and an optional task, the AI proposes a cart, labelled an "AI-generated proposal", which goes through the same deterministic check as any other cart. In an "informed" mode the agent sees the mandate's limits; in an "uninformed" mode it does not.
- The AI never approves, authorizes, or captures anything. Without an Anthropic key, the AI features report that they are off, and the manual form and the simulated shop remain.
Stack. One Fastify API process, a pure TypeScript domain package with the policy rules, and a Next.js web app. The browser talks only to the web app, which forwards to the API, so credentials never reach the browser.
Built with: TypeScript, Node.js 24, Fastify, Next.js, React, Zod, Vitest, the PayPal Sandbox REST API, the Anthropic API.
Challenges I ran into
- A timeout is not a failure. A request to PayPal that gets no answer may still have reached it. Ostensia records the request before sending it, never retries on its own, and reads the attempt back when the person asks, instead of guessing that nothing happened.
- Making repeats safe. The caller chooses the attempt id, so sending the same request twice returns the same attempt, and a different cart under the same id is refused.
- An order made with a saved payment method comes back already authorized. Ostensia has to inspect the real order and authorization state rather than issue a second authorization blindly.
- First-time vaulting needs the buyer's manual approval, which is why judges set up their own sandbox vault (the README explains how), and nothing is shared.
- A model is not deterministic. The AI agent sometimes stays within the limits and sometimes does not, so nothing the model says decides anything: the same code checks every cart.
- Disputes. Creating a dispute in the sandbox failed for a reason that was not established, so dispute handling was kept out of the demo rather than faked.
- Honest labels. Every panel says where its content came from: the AI, Ostensia's own code, the simulated shop, or PayPal Sandbox. The PayPal label appears only where a PayPal request was actually sent.
Accomplishments that I'm proud of
- A complete, real PayPal Sandbox path (authorize, capture, void) controlled by deterministic checks, with a final check that notices a revoked mandate and voids instead of capturing.
- An audit record that logs each PayPal request before it is sent and a gate whose state can be rebuilt from it.
- A run-it-yourself setup that shares no credentials: judges use their own PayPal sandbox and, optionally, their own Anthropic key.
- An interface that keeps AI-generated, Ostensia-checked, simulated, and PayPal content visibly separate.
What I Learned
A model can propose, but a rule should decide. The AI never gets a tool or an approval, so nothing it says can authorize a payment, and every cart goes through the same deterministic check.
"No answer" is not "failed". A request that times out may still have gone through, so Ostensia writes each PayPal request to the log before sending it, never retries on its own, and reads the real state afterwards. We also learned not to trust a success code on its own: a void is confirmed by reading the authorization back.
Every label on the screen is a claim. The PayPal Sandbox badge appears only where a request was actually sent. And a passing test suite only shows that the code agrees with the tests, so we broke rules on purpose to check that the tests could fail.
What's next for Ostensia
Current limitations
- Sandbox only; not production-ready. One local process, no database, no accounts or authentication.
- The audit log does not record where a cart came from, nor, per attempt, when a mandate was revoked.
- A few attempt states (an unknown order outcome, an order without exactly one authorization, a PayPal-rejected write, and a record mismatch) have no way forward in the API yet; the page says so and retries nothing.
- The AI agent's behaviour was sampled, not guaranteed; the deterministic check is what decides.
- PayPal disputes and webhooks are not part of this demo and are not claimed.
Future work: per-attempt revocation records, component tests for the interface, a hosted demo, and extending the checks to more kinds of purchase terms.
Testing instructions (for Devpost's testing field)
The fastest way to see it is the demo video. To run it, follow the README in the repository: install with pnpm (Node 24), run pnpm test (no credentials needed), then create a PayPal Sandbox app and a sandbox buyer vault, fill apps/api/.env, and start the API and the web app. An Anthropic key is optional. No credentials are provided or shared: judges use their own sandbox credentials.
Repository: (https://github.com/J0shcodes/ostensia) ยท Licence: MIT
Built With
- anthropic
- fastify
- nextjs
- node.js
- pnpm
- react
- typescript
- vitest
- zod
