Usage and quotas
Usage statistics, quotas and credit mode.
On this page
Usage statisticsLink to this section
Every generation — text and image, including aborted and failed runs — is logged to a hidden AI Usage collection (systhema-ai-usage): kind, mode, action, provider and model, token counts, an estimated cost (built-in list prices, correctable via pricing), the prompts, inputs and outputs (stored verbatim but length-capped for inspection, not archival), and for images the source/result upload references. Documents are immutable and written server-side only; the ai.usage.read gating below is the privacy boundary for this content. A run that finds nothing to generate (empty target) short-circuits to success without reserving a quota slot or billing.
The log is gated by the ai.usage.read capability, which no built-in role except dev holds (via its * wildcard) — admins and editors never see it, and the collection is hidden from their admin nav entirely. Grant the capability to a custom role to share it deliberately.
Quotas (subscription limits)Link to this section
quota turns the usage log into enforced subscription limits. Counters cover the current billing period (computed in UTC from period), count every billed generation — completed, aborted, and failures that occurred after a successful (billed) provider call — and reset automatically when the period rolls over:
ai: {
text: { ... },
quota: {
text: 200, // 200 text generations per period
image: 50, // 50 images per period (0 = images not in the plan)
period: { interval: 'month', startDay: 1 },
},
}Once a limit is hit, the matching endpoint blocks with HTTP 429 and a human-readable message (including the reset date) that surfaces directly in the admin UI. Enforcement counts persisted usage plus in-flight generations, so concurrent requests can't slip past the last remaining slot (in-flight tracking is per server instance; the persisted log is the cross-instance source of truth).
Whenever AI is enabled, a compact AI usage panel is pinned to the bottom of the admin sidebar (admin.components.afterNavLinks) — always visible while editing, showing per-kind (or pooled-credit) usage for the current period and the reset date. Any signed-in admin user sees it; it carries no costs and no per-generation detail. With quota limits set, the rows become used/limit progress bars; without, they show plain counts marked "Unlimited".
An AI usage dashboard widget (admin.dashboard.widgets) with the same data is also registered — but it is not placed in the default dashboard layout, because the sidebar panel already surfaces usage. To show it on the dashboard, add it from the dashboard editor. A consumer's own defaultLayout is never touched.
When a limit is set, the widget also shows a pace-based forecast for the binding metric (the pooled credit balance in credit mode, otherwise whichever capped kind is pacing hottest): how much of the allowance is used, how much is projected to remain in reserve at the current burn rate, the countdown to reset, and a verdict — "Lasts until reset" when the projection stays within budget, or "Runs out in ~Nd" when it doesn't.
A compact version of the same panel is also pinned to the bottom of the admin sidebar (via admin.components.afterNavLinks), so editors can watch their usage move while they work without leaving the page. It refreshes on a light poll, on window focus and after each generation, is collapsible (the collapsed state persists per-user through Payload's native preferences — account-tied, so it survives refreshes and follows you across devices), and notes that the allowance is shared across all users. The quota/credits are project-wide — every user draws from the same pool. Both surfaces share one data hook and never show USD.
Set packageName to label the plan you sell (e.g. "Systhema AI Free" / "Systhema AI Pro"). It appears as a heading on the usage widget and is woven into the "limit reached" message when a quota runs out (e.g. "Your Systhema AI Pro plan's text generation limit for this period is used up…"). Purely cosmetic — it has no effect on what is allowed.
Credit modeLink to this section
For plans where per-kind counts can't bound cost — a long translation costs several times a short meta title, and one image can cost as much as dozens of texts — set quota.credits to switch to a single pooled credit balance instead:
ai: {
text: { provider: 'openai', model: 'gpt-5.5', /* … */ },
image: { provider: 'openai', model: 'gpt-image-2' },
quota: {
credits: 5000, // pooled budget per period (≈ $5 at the default creditValue)
// creditValue: 0.001, // USD per credit (hidden from editors); default 0.001
period: { interval: 'month', startDay: 1 },
},
}Each generation debits actual_cost ÷ creditValue credits, computed from the same token/image pricing used for the usage log — so spend tracks real provider cost regardless of kind. creditValue is never shown to editors; it only sets the granularity (0.001 ⇒ a typical text generation ≈ 1 credit, a $5 budget ≈ 5,000 credits). When credits is set it supersedes the text/image counts: the gate blocks once the pooled balance is exhausted, and the widget shows a single Credits balance with a (?) tooltip of this site's average credit cost per text / per image. Leave credits unset (the default) to stay in count mode.
The widget and /systhema/ai/usage endpoint are visible to any authenticated admin user and never expose USD — dollar amounts stay in the developer-only usage collection.