Problem
The REST API advertises per-tier allowances ("API Starter — 1,000 requests/day") but enforces only a global per-IP window (600/60s) plus a per-key MCP limiter; the apiRateLimit entitlement field is declared and never read. Result: a truth-in-marketing gap, Starter/Business identical at the network layer, and Enterprise's advertised 1,000/60s unreachable behind the 600/60s per-IP cap.
Resolved design — Phase 1
Full plan + code grounding: docs/plans/2026-06-30-001-feat-per-tier-api-rate-limits-plan.md.
Two distinct limit types at the single gateway chokepoint (server/gateway.ts:1034):
- Per-minute burst (infra/abuse) → hard 429, value-sourced from
apiRateLimit (Starter 60 / Business 300 / Enterprise 1,000 per 60s).
- Daily usage meter (commercial allowance) → counts every served request, never rejects at the allowance (new
apiDailyAllowance: 1,000 / 10,000 / unlimited).
- Safety ceiling at 10× the allowance → hard 429 (runaway/cost protection; Enterprise none).
Eligibility keys on isUserApiKey / isEnterpriseAuth (user wm_ keys carry no keyCheck.kind); eligible keys bypass the per-IP cap in enforce mode only (shadow keeps it active). Fail-open throughout. Ships shadow-first behind API_RATE_LIMIT_ENFORCE. Marketing + docs/usage-rate-limits.mdx reframed to "included allowance," single-sourced from the catalog.
Overage billing is Phase 2 → #4560.
Acceptance criteria (revised — the daily axis meters, it does not hard-cap)
Open decisions before the enforce flip
10× ceiling justification, telemetry-durability audit query, "included" copy timing, 5xx-in-meter, per-account identity vs orgs — see plan OQ1–OQ5. (Phase 1 ships in shadow regardless; these gate the enforce flip, not the merge.)
Problem
The REST API advertises per-tier allowances ("API Starter — 1,000 requests/day") but enforces only a global per-IP window (600/60s) plus a per-key MCP limiter; the
apiRateLimitentitlement field is declared and never read. Result: a truth-in-marketing gap, Starter/Business identical at the network layer, and Enterprise's advertised 1,000/60s unreachable behind the 600/60s per-IP cap.Resolved design — Phase 1
Full plan + code grounding:
docs/plans/2026-06-30-001-feat-per-tier-api-rate-limits-plan.md.Two distinct limit types at the single gateway chokepoint (
server/gateway.ts:1034):apiRateLimit(Starter 60 / Business 300 / Enterprise 1,000 per 60s).apiDailyAllowance: 1,000 / 10,000 / unlimited).Eligibility keys on
isUserApiKey/isEnterpriseAuth(userwm_keys carry nokeyCheck.kind); eligible keys bypass the per-IP cap in enforce mode only (shadow keeps it active). Fail-open throughout. Ships shadow-first behindAPI_RATE_LIMIT_ENFORCE. Marketing +docs/usage-rate-limits.mdxreframed to "included allowance," single-sourced from the catalog.Overage billing is Phase 2 → #4560.
Acceptance criteria (revised — the daily axis meters, it does not hard-cap)
X-RateLimit-*+Retry-After.Retry-Afterto next UTC midnight.isUserApiKey/isEnterpriseAuth(notkeyCheck.kind); anonymous /wms_/ downgraded keys keep the per-IP path.X-RateLimit-Limit/Remaining/Reset+Retry-After./promarketing +docs/usage-rate-limits.mdxreference the same catalog numbers.Open decisions before the enforce flip
10× ceiling justification, telemetry-durability audit query, "included" copy timing, 5xx-in-meter, per-account identity vs orgs — see plan OQ1–OQ5. (Phase 1 ships in shadow regardless; these gate the enforce flip, not the merge.)