A bounded agentic reverse marketplace for price, delivery, warranty, and test-mode payment.
Built for the Razorpay AI Buildathon 2026.
Online shopping still makes the buyer do the work:
open tabs -> compare sellers -> trade off price vs delivery vs warranty -> decide manually.
UNDERBID flips that flow.
The buyer states a product, a hard budget, a delivery limit, and what matters most.
Then three seller agents compete for the purchase.
Each seller has private economics — opening price, floor, concession behavior, delivery options, warranty options, and add-on costs — and can never violate its own constraints.
The buyer never accepts an offer that violates theirs.
AI chooses the strategy. Code controls the economy. The user controls the money.
The retail anchor seeds the synthetic seller market.
The buyer budget is a separate hard acceptance ceiling — it is not used to set seller prices.
Each seller exposes:
- current offer,
- round number,
- AI-selected strategy,
- strategy rationale,
- delivery,
- warranty,
- add-ons,
- offer history,
- hard-constraint violations,
- buyer utility when valid.
The seller-private floor and cost are never exposed to the buyer.
The engine runs a maximum of 5 simultaneous rounds.
Every seller in round R only sees the completed public state of round R-1, so execution order does not give one seller an unfair informational advantage.
In this run:
| Field | Result |
|---|---|
| Typical retail anchor | ₹50,000 |
| Buyer hard budget | ₹45,000 |
| Winner | SELLER B |
| Final price | ₹44,327.60 |
| Delivery | 3 days |
| Warranty | 18 months |
| Rounds | 5 |
The winner is not necessarily the cheapest seller.
Only offers that satisfy every hard constraint are eligible, and the valid offer with the highest weighted buyer utility wins.
The frontend never tells the backend what amount to charge.
It sends the negotiation ID. The backend reloads the persisted final contract and creates the Razorpay Test order from the stored price.
DEAL_FOUND
v
buyer explicitly authorizes
v
backend loads persisted final price
v
Razorpay Test order
v
checkout
v
server-side signature verification
Razorpay is used in Test Mode. No real money is moved.
Checkout success is not trusted blindly.
UNDERBID verifies the Razorpay response server-side before recording the payment as verified.
A financial agent should know when not to transact.
With the same ₹50,000 retail anchor but a ₹30,000 hard budget:
![]() |
![]() |
All three sellers remain above the buyer's hard budget after the allowed negotiation rounds.
UNDERBID does not manufacture a deal.
NO DEAL
5 / 5 rounds completed
No payment action initiated
That is a successful failure path, not an application error.
The LLM is used for bounded seller strategy selection.
Allowed actions:
HOLD
CONCEDE_PRICE
ADD_VALUE
IMPROVE_TERMS
The model can also provide a short rationale.
It cannot directly decide:
price
discount amount
seller floor
buyer budget
utility score
contract validity
winner
payment amount
payment authorization
| Decision | AI strategy layer | Deterministic engine |
|---|---|---|
| Choose negotiation tactic | Yes | |
| Explain tactic rationale | Yes | |
| Calculate exact offer price | Yes | |
| Enforce seller floor | Yes | |
| Enforce buyer budget | Yes | |
| Enforce delivery limit | Yes | |
| Compute buyer utility | Yes | |
| Select winner | Yes | |
| Derive payment amount | Yes | |
| Verify Razorpay response | Yes |
LLMs are useful for qualitative strategy.
They are a poor place to put financially consequential arithmetic or hard permissions.
So UNDERBID uses:
LLM strategy proposal
v
typed / bounded action
v
deterministic economic engine
v
hard-constraint validation
v
structured offer
Money is never probabilistic.
flowchart LR
B[Buyer Request] --> SF[Seller Factory]
SF --> A[Seller A<br/>Private Economics]
SF --> C[Seller B<br/>Private Economics]
SF --> D[Seller C<br/>Private Economics]
A --> G[Groq Strategy Layer]
C --> G
D --> G
G --> E[Deterministic<br/>Negotiation Engine]
E --> V{Hard constraints}
V -->|invalid| X[Reject offer]
V -->|valid| U[Buyer utility]
U --> W[Highest-value<br/>valid contract]
W --> P[Explicit payment authorization]
P --> R[Razorpay Test Checkout]
R --> S[Server-side verification]
Each synthetic seller privately owns parameters such as:
cost price
opening price
hard floor
concession rate
delivery options
warranty options
add-on costs
personality / strategy
Important invariants:
cost < floor < opening
seller never prices below floor
accepted price never exceeds buyer budget
accepted delivery never exceeds buyer limit
The current prototype does not scrape live merchant pricing.
A user-provided typical retail price anchors the synthetic seller market, while the buyer budget remains an independent hard constraint.
UNDERBID negotiates more than one number.
A contract contains:
price
delivery days
warranty months
add-ons
The buyer controls the relative importance of:
- price,
- delivery,
- warranty.
Hard constraints are checked before utility.
So a fantastic warranty can never compensate for an offer that exceeds the buyer's budget.
If valid offers have equal utility:
- higher buyer utility,
- lower price,
- faster delivery,
- longer warranty,
- seller name.
No random winner selection.
The backend persists structured negotiation events and streams them to the frontend over WebSockets.
Events include:
ROUND_STARTED
OFFER_CREATED
SELLER_WALKED
DEAL_FOUND
NO_DEAL
PAYMENT_ORDER_CREATED
PAYMENT_VERIFIED
That makes the negotiation auditable instead of hiding the final outcome behind an LLM response.
UNDERBID was stress-tested across 1,000 randomized synthetic markets covering different retail anchors, budgets, delivery constraints, preference weights, and seller economics.
| Metric | Result |
|---|---|
| Markets tested | 1,000 |
| Economic invariant violations | 0 |
| Seller-floor violations | 0 |
| Buyer-budget violations | 0 |
| Delivery-constraint violations | 0 |
| Sellers that won at least once | 3 / 3 |
| Distinct winning prices | 348 |
| Agreements | 348 |
| No-deal outcomes | 652 |
| Average terminal round | 4.06 |
| Median terminal round | 5 |
| P95 terminal round | 5 |
The high no-deal rate is intentional.
The stress-test distribution deliberately includes buyer budgets as low as 60% of the retail anchor and delivery requirements as strict as 1 day.
This is a safety/invariant stress test, not a marketplace conversion benchmark.
python -m pytest -vBackend parity / privacy test: PASS
cd frontend
npm run lint
npm run buildESLint: PASS
Next.js production build: PASS
TypeScript: PASS
Static generation: PASS
UNDERBID deliberately separates agreement from payment authorization.
The frontend does not send:
charge = ₹44,327.60
It sends only the negotiation identity.
The backend reloads the persisted winner and final price, then creates the Razorpay Test order from those stored values.
| Property | Enforcement |
|---|---|
| Seller cannot cross floor | deterministic seller engine |
| Invalid buyer contract cannot win | hard validation before utility |
| Frontend cannot choose charge amount | backend reloads persisted agreement |
| Payment cannot happen before agreement | payment endpoint requires final winner |
| Duplicate order creation is bounded | existing payment-order state reused |
| Razorpay secret stays server-side | environment variables |
| Checkout response is verified | server-side Razorpay signature verification |
| LLM failure does not corrupt money logic | deterministic fallback |
| Important actions are inspectable | structured event/audit trail |
| Layer | Technology |
|---|---|
| Frontend | Next.js 16, React, TypeScript |
| Styling | Tailwind CSS |
| Visualization | Recharts |
| Backend | FastAPI, Python |
| Realtime | WebSockets |
| Validation | Pydantic |
| Persistence | SQLite + SQLModel |
| Money arithmetic | Python Decimal |
| Negotiation | Custom deterministic engine |
| AI strategy | Groq + Qwen |
| Payment | Razorpay Orders + Standard Checkout |
| Testing | Pytest, ESLint, Next.js production build |
No LangChain, CrewAI, AutoGen, Kafka, or other heavyweight orchestration layer is required.
The market state machine and economic rules are implemented directly so they remain easy to inspect.
git clone https://github.com/ziennaa/underbid.git
cd underbidpython -m venv .venv
.venv\Scripts\Activate.ps1
pip install -r requirements.txtCreate a root .env:
GROQ_API_KEY=your_key_here
GROQ_MODEL=qwen/qwen3.6-27b
RAZORPAY_KEY_ID=your_test_key_id
RAZORPAY_KEY_SECRET=your_test_key_secretUse Razorpay Test Mode credentials only.
Start the API:
uvicorn backend.main:app --reloadBackend:
http://127.0.0.1:8000
API docs:
http://127.0.0.1:8000/docs
In another terminal:
cd frontend
npm install
npm run devOpen:
http://localhost:3000
POST /api/negotiations
GET /api/negotiations/{id}
POST /api/negotiations/{id}/start
WS /ws/negotiations/{id}
POST /api/negotiations/{id}/payment/order
POST /api/negotiations/{id}/payment/verify
underbid/
├── backend/
│ ├── agents/
│ │ └── strategy.py
│ ├── database.py
│ ├── main.py
│ ├── manager.py
│ ├── models.py
│ ├── payments.py
│ ├── schemas.py
│ ├── seller_factory.py
│ └── service.py
│
├── negotiation/
│ ├── buyer.py
│ ├── engine.py
│ ├── seller.py
│ ├── utility.py
│ └── validate.py
│
├── frontend/
│ └── src/
│ ├── app/
│ ├── components/
│ └── lib/
│
├── tests/
│ └── test_phase3.py
│
├── docs/
│ ├── screenshots/
│ └── validation/
│
├── validate_markets.py
├── requirements.txt
└── README.md
- genuine dynamic seller competition,
- private synthetic seller economics,
- multi-round negotiation,
- bounded LLM strategy decisions,
- deterministic hard-constraint enforcement,
- multi-attribute buyer utility,
- real-time WebSocket market visualization,
- graceful
NO_DEAL, - persisted audit events,
- Razorpay Test order creation,
- explicit buyer authorization,
- server-side payment signature verification.
- live Amazon / Croma / Reliance prices,
- real merchants behind Seller A/B/C,
- production inventory,
- real-money settlement,
- production fulfilment,
- merchant authentication.
The synthetic market exists so the negotiation mechanism can be stress-tested independently.
A production marketplace could replace the seller factory with merchant inventory/pricing adapters while preserving the same negotiation and payment boundaries.
The fastest way to prove UNDERBID is not scripted:
- Enter a product and retail anchor.
- Choose a budget.
- Set your own price / delivery / warranty priorities.
- Randomize the market.
- Run the negotiation.
- Run it again.
- Lower the budget until the system refuses the deal.
- On a successful market, explicitly authorize the Razorpay Test payment.
You should not be able to predict the winner before the market runs.
That is the point.








