Skip to content

Design — Unit economics & cost model

  • Date: 2026-05-10 (refresh for ADR-0059 v2.1)
  • Sourced from: ADR-0015 D8 (per-query cost), ADR-0018 (cost ceilings), Cloudflare/Anthropic/Cohere public pricing pages
  • Conversion rate used: 35 THB / 1 USD
  • Status: Source of truth for pricing decisions. Updated whenever a stack component changes price (Anthropic, Cohere, Cloudflare) or a pricing ADR is accepted.
  • Pricing model output: ADR-0038 — Pricing & SKU Model (Accepted 2026-05-03), as amended by ADR-0059 — Pricing v2.1 (Proposed 2026-05-10, additive parts shipped). This doc is the cost model; the pricing ADRs are the price models that use it as input.

This doc answers: what does it cost us to serve one query / one document / one tenant, and what is the gross margin per plan? It is the input to ADR-0038. Pricing decisions made without consulting this doc are not legitimate.


1. Per-query cost (the dominant variable cost)

Section titled “1. Per-query cost (the dominant variable cost)”

A single chat turn fans out across the RAG pipeline (ADR-0004). Costs:

Component Unit price Per-query usage Cost
Embedding (bge-m3, Workers AI) $0 within free tier 1 query embed ~$0
Vectorize query $0.01 / 1M dims queried 50 chunks × 1024 dims = 51K ~$0.0000005
D1 reads (history, chunks, tenant) $0.001 / 1M reads ~20 reads ~$0.00000002
D1 writes (msg, telemetry) $1 / 1M writes ~5 writes ~$0.000005
KV reads/writes (rate limit, presence) $0.50–5 / 1M ~3 ops ~$0.000002
Workers requests $5 / 10M 1 ~$0.0000005
Cohere Rerank v3.5 $2 / 1k searches 1 $0.002
LLM — Haiku 4.5 (default) $1/MTok in, $5/MTok out 2.5K in + 120 out $0.003
Total (Haiku, default path) ~$0.005

Sources: ADR-0015 §D8, Anthropic + Cohere + Cloudflare price pages 2026-05.

Two LLM calls add up: if the query rewriter fires (not gated by ADR-0018 §2c), add ~$0.001. If contextual retrieval misses cache, that’s ingest cost, not query cost.

Sonnet 4.5/4.6 path (llm_route override): LLM cost rises to ~$0.009 → total ~$0.011/query. Roughly 2.2× more expensive than Haiku.


2. Per-document ingest cost (one-time, amortized)

Section titled “2. Per-document ingest cost (one-time, amortized)”

Per ADR-0004 + ADR-0018 §2a:

Component Unit price Per-doc usage Cost
Contextual retrieval (Haiku) $0.0015 / chunk (1K in + 100 out) ~10 chunks/doc $0.015
Embedding (bge-m3) free tier 10 chunks $0
Vectorize ingest included in storage 10 vectors $0
D1 + R2 writes negligible one doc <$0.0001
Total ~$0.015 / doc, one-time

Storage is essentially free at our scale: 10 chunks × 4 KB Vectorize + ~10 KB D1 + ~1 MB R2 = under $0.0001/doc/month.

Important caveat (ADR-0099). Contextual retrieval is hard-gated at markdown length < 100K chars in apps/app/src/lib/rag/ingest.ts:189. Any document above that size silently skips the contextualization step — the $0.0015/chunk line above only fires on small docs. This is why raising per-plan upload caps to 5/10/25/50 MB (ADR-0099) did not blow up ingest spend: large files were already bypassing the expensive step. The implication is product quality, not cost: large uploads ingest cheaply but retrieve worse than their smaller counterparts. A follow-up ADR will rework contextual retrieval to scale beyond 100K chars with a per-tenant monthly ingest-cost budget enforced before contextualization fires; at that point the $0.0015/chunk figure becomes load-bearing again (roughly $1.50–$5.00 per 50 MB doc at typical chunk counts) and the per-plan budget needs to be modeled here.


3. Per-tenant fixed cost (regardless of usage)

Section titled “3. Per-tenant fixed cost (regardless of usage)”
Item $/tenant/month
Allocated Workers Paid base ($5/mo ÷ tenant count) <$0.10 at >50 tenants
Sentry events allotment <$0.05
Resend transactional email allotment <$0.05
Stripe billing fees 2.9% + ฿10 per invoice
Total fixed overhead per active tenant ~$0.20

Inactive tenants (0 queries/month) cost ~$0 — D1+R2 storage is the only ongoing draw, and a dormant tenant uses kilobytes.


4. Gross margin per plan (LLM is the dominant cost)

Section titled “4. Gross margin per plan (LLM is the dominant cost)”

Plan limits per billing.ts:31-36. Note: limits in code are interpreted as monthly (error message uses เดือน); marketing page advertises daily. This is a known mismatch — see §7.

Plan Price ฿ Price $ Queries/mo LLM cost Ingest cost (full doc cap) GM % GM $
Free 0 0 100 $0.50 20 × $0.015 = $0.30 -∞ -$1
Team 3,900 $111 2,000 $10 500 × $0.015 = $7.50 (one-time) ~91% $101
Business 19,900 $569 10,000 $50 5,000 × $0.015 = $75 (one-time) ~91% $519
Enterprise (typical contract) 40,000 $1,143 unbounded risk unbounded unknown unknown

Ingest is one-time, so on a steady-state monthly P&L only LLM cost recurs. Free is the loss leader; ~$1/month CAC per Free tenant is acceptable.

At 100% quota, doubled conversation length, Sonnet upgrade:

Section titled “At 100% quota, doubled conversation length, Sonnet upgrade:”

Worst realistic case: long answers + Sonnet route (Enterprise customer pushes for higher quality).

Plan LLM cost GM %
Team 2,000 × $0.011 × 2 = $44 ~60%
Business 10,000 × $0.011 × 2 = $220 ~61%
Enterprise (10K queries example) $220 81%

Margin floor stays comfortably above 50% in stress case for Team/Business. Enterprise has no floor — a tenant doing 100K queries/mo on Sonnet costs $2,200 against a $1,143 contract. Negative margin. See §6.

4b. Per-resolution margin (ADR-0038 billable unit, ADR-0059 amended)

Section titled “4b. Per-resolution margin (ADR-0038 billable unit, ADR-0059 amended)”

ADR-0038 changes the billable unit from query → resolution (one conversation that ended successfully). A resolution averages 3–5 queries (turns × tool calls; see ADR-0017), so per-resolution LLM cost ≈ $0.015–0.025 on the Haiku path. On Sonnet (~2.2× LLM cost) per-resolution cost is ≈ $0.033–0.055.

Margin at full included quota, 4 queries/resolution average, all-Haiku path. Numbers reflect the ADR-0059 SKU table:

Plan Price ฿/mo Included res LLM cost @ Haiku Overage rate GM % at included
Free 0 100 $2 hard cap -$2 (CAC)
Team 3,900 1,000 $20 ฿4/res ~82%
Business 19,900 5,000 $100 ฿3/res ~82%
Growth (ADR-0059 §6) 34,900 12,000 $240 ฿2.50/res ~76%
Enterprise Contact sales 25,000+ $500 ฿2/res ~64% (negotiable)

Overage economics: at ฿3 (~$0.086) per Business overage resolution and $0.020 LLM cost per resolution, overage GM is ~77%. Growth’s ฿2.50 overage at the same cost = ~77%. Enterprise’s ฿2 overage = ~71%. Overage is healthy revenue, not loss-leader.

Sonnet (ADR-0059 §5) — 2× meter, not 2× overage. Sonnet resolutions count 2× toward the included pool, then bill at the standard plan overage rate (no separate doubling). A Business customer using 100% Sonnet exhausts the included pool at 2,500 resolutions — same revenue, ~10% higher LLM cost ($110 vs $100), still ~81% GM. Cleaner customer mental model than the old “free Sonnet under quota, 2× overage above”; closes the margin-erosion edge case where heavy-Sonnet usage stayed under cap forever.

Team seat add-on (ADR-0059 §2) — near-100% GM line item. Each ฿800/seat/month carries ~$0 marginal cost (one D1 membership row, Workers requests are below threshold). A Team customer at max add-on (5 extra) pays an additional ฿4,000/mo at >99% GM, taking the all-in to ฿7,900/mo while the cap is still 60% below Business — protects the Business upgrade incentive at the upper end of the Team band.

If avg turns/resolution doubles (long compliance Q&A), GM drops ~10pp at every tier. Still safe across all four paid tiers.


Meter (pay scales with this) — per ADR-0038 + ADR-0059:

  • Resolutions / month (primary; Sonnet routes count 2× per ADR-0059 §5)
  • API queries / month (secondary; 1 API query = 0.2 resolution equivalent)
  • Documents stored
  • Seats (admin users); Team gets a per-seat add-on at ฿800/seat for seats 6–10 (ADR-0059 §2)
  • Workspaces (per ADR-0039, pending)

Floor (free at every plan, capped only for abuse prevention):

  • Allowed origins (CORS domains)
  • API requests (counted as queries when they hit /api/chat; otherwise free)
  • Widget loads / page views
  • D1+R2 storage under generous thresholds (no plan currently hits caps)
  • Conversations stored (retention enforced, count not metered)

The principle: charge on the axis that scales LLM spend (queries) or perceived value (docs, seats, workspaces). Don’t invent meters on free-to-serve resources — customers smell it.


Status: addressed by ADR-0038 (Proposed) — Enterprise gets 25K included resolutions + ฿2 overage with negotiated quotas_json overrides per contract; AI Gateway hard cost cap remains belt-and-suspenders.

PLAN_META.enterprise.queries = -1 (billing.ts:35). One enterprise customer running 200K queries/mo on Sonnet costs $4,400 in LLM spend against a ~$1,143 baseline contract.

A bad actor could create N Free tenants, each consuming up to $1/mo, to extract free LLM access. Partially mitigated by:

  • Email-based signup (one tenant per verified email)
  • Anonymous rate limit 5/h (ADR-0018)
  • Free monthly cap of 100 queries

Not yet mitigated:

  • Same-org multi-account creation (need device fingerprint or domain-based dedup)
  • Programmatic tenant creation via API (need CAPTCHA or admin approval)

Status: closed. ADR-0038 §4 gated Sonnet to Business+; ADR-0059 §5 replaced the “double the overage rate” rule with “Sonnet resolutions count 2× toward the included pool” (sonnetWeightedResolutionCount() in billing.ts). Heavy-Sonnet usage now visibly draws down quota instead of staying free under cap.

tenants.llm_route defaults to 'gateway-anthropic' (Haiku); switching to a Sonnet route requires a Business+ tier (gated in getEffectiveQuotas).

If conversation history grows unbounded, messages_in rises with each turn. Currently capped by ADR-0017 sliding window, but window size is not plan-gated. Worth verifying that Free tier window ≤ 8 turns to keep tokens-per-turn bounded.


7. Known mismatches to fix before next pricing change

Section titled “7. Known mismatches to fix before next pricing change”
Problem Found in Status
PLAN_META.queries is monthly in code, but pricing page advertises daily (15× more generous) billing.ts:33-34 vs pricing.astro:36,59 Resolved by ADR-0038 (Proposed) — billing unit = monthly resolutions; daily soft cap remains as abuse layer per ADR-0018
Daily soft caps in ADR-0018 (500/5,000) ≠ monthly hard caps in code (2,000/10,000) rate-limit.ts vs billing.ts Resolved by ADR-0038 §1 — two-layer model documented: daily = abuse, monthly = billing
Cache-Control envelope assumes ~50 tenants; recompute fixed-cost share at 5, 50, 500 §3 above Open — refresh when tenant count crosses milestones
No per-tenant quotas_json override yet Schema Resolved by ADR-0038 — Schema additions (Proposed); shipped during ADR-0038 implementation

If LLM cost changes by 2×, these are the resulting Team/Business margins (queries-only, full quota):

LLM $/query Team GM % Business GM %
$0.005 (Haiku, current) 91% 91%
$0.010 (Haiku × 2 — long convs) 82% 82%
$0.011 (Sonnet) 80% 81%
$0.020 64% 65%
$0.030 46% 47%
$0.050 10% 12%

Survival floor: ~$0.045/query. Above this, Business plan goes underwater at full utilization. Any future LLM choice must be benchmarked against this number.


  • Sales & marketing CAC — not yet measurable; will be added once we have ≥10 paying customers.
  • Support cost per ticket — currently zero (founder-led). Will become real at scale.
  • Compliance overhead for regulated tenants (SOC 2 audit cost amortization, DPO time) — Enterprise-only, modeled separately when first such contract signs.
  • Annual discount math (PRD mentions 20%) — should be computed against this doc’s GM floor before honored.

This doc must be updated when any of the following changes:

  1. Anthropic, Cohere, or Cloudflare changes a price we depend on
  2. We change llm_route default or add a new model
  3. We change plan quotas in PLAN_META
  4. Tenant count crosses 10 / 50 / 500 (fixed-cost share recomputes)
  5. We sign an Enterprise contract with custom quotas (record the negotiated floor)
  6. A pricing ADR is accepted (refresh §4b, §5, §6 cross-references)
  7. Pilot mode ends (first paying customer signs up — ADR-0038 §6 protocol kicks back in, grandfathering math becomes load-bearing)

Owner: founder. Reviewer: any engineer touching billing.ts or rate-limit.ts.