Developers
How resell.store is built
What it is
resell.store is a marketplace where every seller gets their own shop on a subdomain, like maya.resell.store, and an AI agent that researches prices, writes listings, answers buyers and haggles within limits the seller sets. Buyers pay through PayPal, the money is held until the item arrives, and then it's released to the seller.
It was built for the PayPal AI Hackathon, and it's open source under the MIT license. The code is on GitHub.
The moving parts
It's deliberately small: one app, one database, one job runner.
- One Next.js app (
apps/app) serves everything people and programs reach: the marketplace, every store subdomain, the seller app, checkout, the public API, these docs and the hosted MCP servers.proxy.tslooks at the host name and sends each request to the right part of the app. - Postgres with pgvector holds all of it, including uploads when there's no object storage. The schema and migrations live in
packages/db(Drizzle). - A Render Workflow (
apps/app/workflows/main.ts) runs the timed jobs: expiring offers, reminders, cancelling unshipped orders, releasing held money, digests and webhook retries. A Render Cron Job starts it every 15 minutes. Without Render, one HTTP call runs the same jobs in-process. - Work that shouldn't slow a page down (research, the store agent's answers, counter-offers, emails) runs in the web process after the response is sent.
- Packages beside the app:
@repo/db,@repo/ui(the design system),@repo/email(React Email templates) and@repo/mcp(the MCP servers, built on the public API).
Where a request goes
One deployment answers on the root domain and a wildcard. The host decides which part of the app handles the request; the path does the rest.
| Request | Handled by |
|---|---|
resell.store/discover | app/(market): the public marketplace |
resell.store/home | app/(app): the seller app, behind sign-in |
resell.store/list/[id]/research | app/(workspace): the listing workspace |
resell.store/checkout/[listing] | app/(checkout): buying and offers |
maya.resell.store/dutch-oven | app/store/[store]/[listing], rewritten from the subdomain |
api.resell.store/v1/listings | app/api/v1/[[...path]] → lib/server/api |
docs.resell.store/dev | app/docs/dev (also at resell.store/docs/dev) |
mcp.resell.store/u/[token] | app/api/mcp/[[...path]] → @repo/mcp |
resell.store/api/paypal/webhook | PayPal's webhook, lib/server/paypal-webhook.ts |
Outside services
Only Postgres and an auth secret are required. Every other service switches a feature on, and the app has a fallback without it, so you can run the whole thing locally with no keys at all.
| Service | What it does here |
|---|---|
| AI model (Anthropic by default, via the AI SDK) | Identifies items, prices them, writes listings, runs the listing chat, the store agent and the negotiator's wording |
| Channel3 | Product catalog: what an item is, what it costs new, second-hand offers |
| Kernel | Headless browsers that read public listings on eBay, Poshmark and Depop for comps and the Shopping sidekick |
| Jina | Embeddings for semantic search, stored in pgvector |
| PayPal (sandbox) | Checkout, the platform fee, held money, payouts, refunds and disputes |
| Resend | Email: sign-in links and notifications |
| Cloudflare R2 | Photo and video uploads |
| Render | Hosting: the web service, Postgres, the workflow and the cron job |