The problem
Restaurant food procurement in Denmark runs on phone, fax, Excel, and a patchwork of supplier-specific ecommerce portals. Every supplier has their own ordering site or app, every site needs its own login, every order gets re-typed into half a dozen separate vendor checkouts. Price lists arrive as PDFs; orders go back as emails. On top of that sits a layer of CRMs and ERPs (e-conomic, Dinero, Microsoft NAV, the supplier's own back-office) that don't talk to any of the ordering channels, so reconciliation is manual, often weeks late, and full of errors. Nobody can compare offers across suppliers in any structured way. The buyer side has no visibility into what each supplier is willing to do; the supplier side has no efficient way to respond to dozens of small RFQs per day; the back-office systems on both ends have no programmatic way to plug in.
On the consumer side: small Danish farms have produce to sell but no infrastructure for coordinated pre-orders or scheduled neighbourhood pickup. Individual farm shops exist, physical farmers' markets exist, what was missing was the digital coordination layer between them: multiple farms shipping to one drop point on a schedule consumers commit to in advance. Existing home-delivery boxes (capital-heavy, thin margins) had plateaued.
The build challenge: a small engineering team shipping a 3-app monorepo with B2B + D2C + admin + mobile in 14 phases. Exactly the kind of scope where every new AI session starts blind and ships the same bug twice without a system to prevent it.
What we built in response
PickNDeal collapses that fragmentation into one structured marketplace, then exposes the marketplace itself to agents and existing back-office systems via an MCP server, an API-key-authenticated REST surface, and HMAC-signed webhooks. Restaurants and suppliers can use the web/mobile app, or their own AI agent (Claude Desktop, Cursor, custom orchestration), ortheir existing ERP/CRM via webhook subscription. Same API surface, same data model. Built with agentic engineering as the delivery model from day one: the platform is agent-native by design. The MCP server is not a bolt-on, it's how everything is built. That's what makes the Phase 9 integration story interesting, buyers and suppliers can plug their existing systems into PickNDeal without any human-in-the-middle, and they can do it in a weekend instead of a quarter.
The architecture
A monorepo (Turborepo + pnpm workspaces) with three apps and four shared packages:
- apps/web, Next.js 16 (App Router). Restaurant + supplier + consumer + public marketing, all under one origin.
- apps/admin, Next.js 16 admin app, role-gated, separate origin.
- apps/mobile, Expo 55 React Native, iOS + Android, both live in stores.
- packages/api, tRPC v11 routers, the single API surface for everything.
- packages/db, Drizzle ORM schema, migrations, and seeders.
- packages/ai, Anthropic SDK client, role-scoped tools, agentic chat loop.
- packages/mcp-server, MCP server exposing the platform to external agents.
What the agentic method delivered
1. AI offer agent (Phase 8)
Suppliers configure auto-offer rules (per category: unit price, lead time, min/max quantity). When a restaurant posts an RFQ, the platform fires the agent within seconds. The agent reads the RFQ, matches against the supplier's rules, and posts a structured offer. The restaurant sees offers from multiple suppliers within minutes of posting, not days. Human-review gate on the supplier side: the supplier can override or auto-accept rules per category.
2. Multi-supplier acceptance (Phase 5)
A restaurant can accept different items from different suppliers in a single order. Theorder_item_assignmentstable is the source of truth: each line item is assigned to exactly one supplier. Payment splits and fulfilment notifications flow per-supplier from there.
3. D2C consumer flow (Phase 7)
Consumers discover farmer group orders by neighbourhood, subscribe with one tap, pay via Stripe (saved cards + business invoicing with Danish VAT/CVR support), pick up from the farmer's designated location. Auto-offer rules fire when a consumer subscription completes the minimum-order threshold, notifying the farmer to confirm.
4. MCP server (Phase 9)
External agents (Claude Desktop, Cursor, custom orchestration) can authenticate via API key and operate the platform: list categories, create RFQs, submit offers, query order state. HMAC-signed webhook delivery for ERP integrations. This is what makes the platform agent-native, buyers and suppliers can integrate their own automation without any human-in-the-middle.
5. Production hardening (Phases 10–14)
AI chat assistant for all roles (SSE streaming + rate limiting). Reorder agent cron with safety boundaries. Push notifications fan-out to consumers within delivery radius. Stripe webhook idempotency. Schema-touch detection in the deploy pipeline (force-rerun of Drizzle db:pushwhen schema files change, not on every deploy). Build-then-swap deploy pattern with health-check rollback.
Production reality. Failure modes caught and codified.
- Live in production at pickndeal.app. Continuous deployment with zero-downtime build-then-swap and automated rollback on failed health checks.
- Mobile apps live: iOS App Store (build 2 reviewed) and Android Play Store (since 2026-04-18).
- 14 phases shipped from initial scaffold to current production. Each phase has its own committed plan plus rules extracted into the project-scoped library.
- Three production-shape failure modes caught in the pre-launch shakedown (deploy was pointing at the production database; no public users on the system yet). The deploy script got hardened before any real user hit the same path. Each retrospective became a rule in the library so the class of failure cannot recur.
Selected rules we extracted
- Schema changes need migration files alongside the consuming code. Splitting a column add and the code that reads it across two commits guarantees a 500 in production. Three failure modes during pre-launch shakedown taught us this. Read the story →
- Build before restart. The deploy script builds to
.next-new/then atomically swaps. The running PM2 process keeps serving the old build the entire time. Health-check before commit; rollback automatically. Read the story → - Fixed-position overlays must portal to
document.body. Any ancestor withtransformsilently convertsposition: fixedinto “positioned against that ancestor.” Caught three different modal failures in one week before we made it a rule. - Reproduce UI bugs in the browser before fixing. A confirmed-by-curl hypothesis is not the same as a confirmed root cause. Three speculative fixes burned three deploys before this became a rule.
The full rule set lives inside the engagement deliverables. 50+ rules in the PickNDeal codebase alone; 75+ across our libraries.
Why this matters for your build
PickNDeal is one shape of agentic system; your shape will be different. What carries over is the method: the tool-surface design, the rule library, the audit-trail-first instrumentation, the human-review gates on the fault line. Whether you're building a marketplace, a procurement system, an internal ops tool, or a customer-facing AI feature. The method holds because the failure modes are the same.
Engineering deep dives from this build
Several of the engineering journal posts use this codebase as their worked example. If you want to see how the patterns above were actually implemented:
- How the AI agent's tool dispatcher actually fires , the typed dispatcher, role-scoped allowlists, and audit trail behind the supplier-facing AI offer agent.
- Why we test AI agents against real APIs by default , the testing posture used across the Stripe Connect flow, webhook idempotency, and the AI offer agent itself.
- 8 weeks of an AI agent in production , week-by-week account of the engineering work around the Phase 10 AI offer agent: what changed, which guards tightened, which assumptions broke.
- Building a human-in-the-loop approval queue for an AI agent , the draft-then-submit pattern that gates every AI-generated offer in PickNDeal before it reaches a restaurant.
- Idempotent webhook handlers in Next.js 16 , the three-rule HMAC + timestamp + event-id pattern behind PickNDeal's Stripe and Resend webhook handling.
- Three production outages from one schema migration , the deploy script we hardened during PickNDeal's pre-launch shakedown so the failure class can't recur.
Want this applied to your system?
Discovery, build, or transformation engagements. We take on a small number per quarter.