How RoasteryHub thinks.
A narrative tour of the data model and the conventions behind every workflow. Read end-to-end in twenty minutes — or jump to a topic.
Pick where to start.
Invite-only onboarding and your first sign-in.
How a roast becomes FIFO lots, automatically.
Tiers, payment terms, and atomic credit checks.
Three sources, one lifecycle, zero spreadsheets.
Weekly, biweekly, monthly. The cron does the work.
Cafés place orders themselves. You see them live.
Per-bean × tier × effective-dated.
Plain English in, safe SQL out, RLS-enforced.
Getting started
RoasteryHub is invite-only. We onboard new roasteries through a short sales conversation — typically twenty minutes over a call. Once your tenant is set up, you receive a sign-in link directly. No self-serve signup form.
Each roastery is a separate tenant. Customers, batches, orders, prices — every row is isolated at the database level via Postgres row-level security. The app layer trusts the database, not the other way around.
Roast batches & inventory
Recording a roast is three numbers: bean, green weight, roasted yield. RoasteryHub computes the loss percentage and auto-splits the yield into sellable FIFO lots (default 5kg each, configurable per roastery).
Each lot has a code (e.g. G0512-001), a remaining weight, and a status (available, reserved, sold, expired). When an order is placed, the oldest available lot is consumed first. Larger orders span multiple lots automatically.
A freshness window (default 14 days) ages each lot from fresh → expiring → expired. Expired lots disappear from order forms but stay in the database for audit.
Customers & credit
Every customer has a tier (Small, Medium, Large), payment terms (Prepay, Net 15/30/60), a credit limit, and a current balance. Tier drives pricing; see Pricing rules below.
Credit headroom = credit limit − current balance. Every order checks the headroom atomically before allocating inventory, in the same Postgres transaction. Over-limit orders receive the status credit_blocked and never ship.
Recording a payment reduces the balance in the same transaction — the next order check sees the updated headroom immediately. No stale reads, no race conditions.
Orders
Orders have one of three sources: manual (created by you in the admin), standing (generated by the daily cron from a recurring schedule), or portal (placed by the café through their customer portal).
All orders share one lifecycle: draft → confirmed → fulfilled, with cancelled and credit_blocked as terminal alternatives. Cancellation reverses both inventory and balance in a single transaction — no manual cleanup, no half-states.
Standing orders
Set a customer to receive a recurring order: weekly, biweekly, or monthly, optionally pinned to a day of the week. Each standing order tracks its own bean, quantity, and delivery cadence.
The Vercel cron runs at 06:00 UTC daily and creates orders for any standing order whose next_run_at has elapsed. Runs are idempotent on (standing_order_id, delivery_date) — if the cron fires twice in a window, only one order is created.
Pause or resume per standing order without losing the schedule. The next eligible cron run picks it back up.
Café portal
Once invited, a café signs in at /portal-signin and sees only their data: their tier pricing, their balance, their credit headroom, their recent orders. They place new orders directly, which appear live in your admin via a Supabase Realtime toast.
The portal uses the same Postgres RLS as the admin — the café simply has a different role. No mirror tables, no sync jobs, no surprises. The same constraints apply: tier-priced beans, credit headroom checks, FIFO allocation.
Pricing rules
Pricing is per-bean × tier × effective_from. The active rule for a customer ordering bean X is the most recent rule for bean X at the customer's tier whose effective_from is on or before today.
Change a rule and the next order uses the new price — existing orders keep their snapshot in the line items, so historical orders never drift. To override a single order, edit the line item price before confirming.
AI assistant
Ask in plain English — Claude Sonnet writes a read-only SELECT against your tenant's data. The query is validated against an allow-list of tables before running, and Postgres RLS enforces tenant isolation as a final guard. Results stream back in real time.
The AI can also draft cupping insights (Claude Haiku) from scored batches, and re-engagement emails (Sonnet) for at-risk customers. All AI traffic is logged with the prompt, the model, the SQL (if any), and the row count returned.
Still have questions?
Email us — a human reads everything, replies within one business day.