Metric Definition & Quota
This is the most subtle concept in the system. Read it carefully before designing tier-gated features.
The three tables
| Table | Role |
|---|---|
agent_metric_definitions | The catalog of meterable things per agent. Immutable slug and kind. |
tier_metric_quotas | The cap each tier grants for each metric. NULL = unlimited, 0 = deny, positive int = cap. |
subscription_usage | Per-subscription running counter for each metric. Resets per billing cycle for rolling metrics. |
subscription_addons | One-cycle or permanent grants on top of the tier quota. Admin-issued. |
usage_ledger | Append-only immutable log of every consume/release event. Idempotent via clientReqId. |
Fixed vs rolling
| Kind | Semantics | Resets? | Supports release? | Example |
|---|---|---|---|---|
fixed | A total allocation. Used count goes up via consume, down via release. | Never | Yes | "5 active knowledge bases" |
rolling | Per-period budget. Resets at currentPeriodEnd (next Stripe period). | Yes | No — returns 422 | "500 messages per month" |
The kind is immutable after metric creation. You cannot convert rolling → fixed or vice versa. You can create a new metric with the right kind and migrate.
Effective quota
When consume runs, it computes:
effectiveQuota = baseQuota + sum(active addons)
Active addons exclude revokedAt IS NOT NULL and (for one_cycle scope) those whose subscription.currentPeriodEnd is past.
NULL baseQuota means unlimited — effectiveQuota is then Infinity for arithmetic, but the API returns null.
Consume — the only chargeable call
POST /me/agents/:agentId/usage/:metricSlug/consume
{
"delta": 1,
"clientReqId": "uuid-v4-from-the-agent",
"metadata": { "sessionId": "abc" }
}
The handler runs one atomic transaction:
- Read subscription. If none active/trialing →
402 PAYMENT_REQUIRED. - Read metric definition. If unknown slug →
404. - Compute
effectiveQuota. - Compute
newUsed = used + delta. - If
effectiveQuota !== null && newUsed > effectiveQuota→429 QUOTA_EXCEEDEDwithdetails: { remaining: 0, resetsAt, effectiveQuota }. No write. - Else write
subscription_usage.used = newUsed. - Insert
usage_ledgerrow (idempotent onclientReqId— duplicates are no-ops). - Return
200 { ok: true, used, effectiveQuota, remaining, resetsAt }.
clientReqId makes retries safe. If your agent retries on a network glitch, the second call returns the same successful envelope without double-charging.