Skip to main content

Metric Definition & Quota

This is the most subtle concept in the system. Read it carefully before designing tier-gated features.

The three tables

TableRole
agent_metric_definitionsThe catalog of meterable things per agent. Immutable slug and kind.
tier_metric_quotasThe cap each tier grants for each metric. NULL = unlimited, 0 = deny, positive int = cap.
subscription_usagePer-subscription running counter for each metric. Resets per billing cycle for rolling metrics.
subscription_addonsOne-cycle or permanent grants on top of the tier quota. Admin-issued.
usage_ledgerAppend-only immutable log of every consume/release event. Idempotent via clientReqId.

Fixed vs rolling

KindSemanticsResets?Supports release?Example
fixedA total allocation. Used count goes up via consume, down via release.NeverYes"5 active knowledge bases"
rollingPer-period budget. Resets at currentPeriodEnd (next Stripe period).YesNo — returns 422"500 messages per month"

The kind is immutable after metric creation. You cannot convert rollingfixed 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:

  1. Read subscription. If none active/trialing → 402 PAYMENT_REQUIRED.
  2. Read metric definition. If unknown slug → 404.
  3. Compute effectiveQuota.
  4. Compute newUsed = used + delta.
  5. If effectiveQuota !== null && newUsed > effectiveQuota429 QUOTA_EXCEEDED with details: { remaining: 0, resetsAt, effectiveQuota }. No write.
  6. Else write subscription_usage.used = newUsed.
  7. Insert usage_ledger row (idempotent on clientReqId — duplicates are no-ops).
  8. 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.

Release — only for fixed

POST /me/agents/:agentId/usage/:metricSlug/release

{ "delta": 1, "clientReqId": "release-abc" }
Metric kindBehavior
fixedDecrement subscription_usage.used (floors at 0). Append a ledger row.
rolling422 UNPROCESSABLE_ENTITY — rolling counters never decrement; they reset at period end.

Period reset

Rolling counters are not reset by a cron. They're reset lazily: the next consume after currentPeriodEnd zeros the row before incrementing.

This is intentional — it avoids a clock-skewed reset job that could race with consume operations.

Setting quotas

PUT /agents/:agentId/pricing/:tierId/quotas/:metricId

{ "quotaLimit": 500 }
ValueMeaning
nullUnlimited
0Explicit deny — feature gated off
Positive intCap
(no row)Treated as 0 (deny)

Always set a row for every (tier, metric) pair, even if it's null for unlimited.

Boolean features

The recommended way to gate a binary feature (e.g. "voice replies") is:

  1. Create a metric voice with kind: fixed, unit: 'boolean'.
  2. Set quota to 0 for the Free tier (deny), 1 for the Growth tier (allow).
  3. Have the agent read effectiveQuota. If > 0, the feature is enabled.
  4. Don't actually consume — just check.

This keeps the marketplace as the single source of truth for tier features and avoids hardcoded tier name checks in the agent.

Addons

POST /admin/usage/:subscriptionId/:metricId/addon (admin only)

{ "amount": 1000, "scope": "one_cycle", "source": "admin_grant" }
  • one_cycle — valid until currentPeriodEnd, then automatically considered inactive on the next consume.
  • permanent — never expires unless revoked.

Revoke with POST /admin/usage/:subscriptionId/:metricId/addon/:addonId/revoke.

Use cases: support gives a customer extra capacity for a week, paid usage-based purchases (future), promotional credits.

Validation rules

OperationFailure conditionStatus
delete metricAny subscription_usage / addons / ledger row references it422 UNPROCESSABLE_ENTITY
release on rollingAlways422
consume with no active subsubscription.status NOT IN ('active','trialing')402 PAYMENT_REQUIRED
consume with unknown slugagent_metric_definitions row absent404 NOT_FOUND
consume over capnewUsed > effectiveQuota429 QUOTA_EXCEEDED

See also