Status codes & limits
Status codes, plan limits, retention, and billing — the operational rules that apply across every endpoint.
Status codes at a glance
| Code | Meaning |
|---|---|
200 | OK (incl. deduped baseline upload) |
201 | Created (new run / new baseline version) |
400 | Malformed JSON or failed validation |
401 | Missing or invalid client credentials |
402 | A write would exceed a hard plan limit (managed billing only) |
404 | Resource not found / not in your repository |
413 | Request body exceeds the size limit |
429 | Too many failed auth attempts — see Retry-After |
Plan limits
When the server runs with managed billing (DUNGBEETLE_BILLING=managed), ingest endpoints are subject to the plan limits of the team that owns the repository. On the Free tier the repository, storage, and snapshot limits are hard: a write that would exceed one is rejected with 402 Payment Required and an actionable message, while existing data is always preserved (over-quota is read-only, never destructive). Paid tiers are soft fair-use — writes are never blocked; usage past the limit only surfaces as a warning (and, on Business+, a storage overage at billing time).
Self-hosted servers (DUNGBEETLE_BILLING=none, the default) never enforce limits — every quota is unlimited and ingest is never metered. See Pricing and GET /api/v1/usage for the current numbers.
Retention. Each plan keeps runs for a fixed window (14 days on Free up to a year on Business; custom on Enterprise). A cron runs npm run retention:prune to delete runs and their report blobs past the cutoff and free the storage they held — baselines are the durable reference and are never pruned. With managed billing off, retention is unlimited and pruning is a no-op, so a self-hosted server keeps everything by default.
Subscriptions (Stripe). The managed cloud turns plans into Stripe subscriptions. It stays completely dormant until an operator sets STRIPE_SECRET_KEY (plus STRIPE_WEBHOOK_SECRET and the per-plan price ids STRIPE_PRICE_STARTER / STRIPE_PRICE_TEAM / STRIPE_PRICE_BUSINESS) — a self-hosted server never needs Stripe. Users subscribe, upgrade/downgrade, and cancel through Stripe Checkout and the customer portal (from the Usage page, /ui/usage, for the active team); plan and status are driven entirely by signature-verified webhooks at:
| Method | Path | Auth | Purpose |
|---|---|---|---|
| POST | /webhooks/stripe | Stripe signature | Subscription lifecycle → plan + status |
The webhook maps checkout.session.completed → subscribe, customer.subscription.updated → plan/status (a past_due subscription makes the team read-only until billing is fixed — never destructive), and customer.subscription.deleted → revert to Free. Invalid signatures are rejected with 400 and change nothing.