Budgets and Quotas
A metric's quota block turns it into a budget: a per-user counter over a
reset window (day, month, billing_cycle, or prepaid) checked
against a soft_limit (warn) and a hard_limit (refuse).
One thing to hold on to - the quota counter and the usage buckets are separate ledgers:
- Ingesting an event adds to both: the time buckets that
usage,top, and reports read, and the quota window'sused. - Committing a reservation adds only to the quota window's
used- verified: after committing 500 units,checkreportedcurrent_usage: 1120whileusagestill totalled620.
So for one unit of work, account through one path: ingest events for usage that must appear in rollups and billing, or reserve-and-commit when you need units held up front. Doing both for the same tokens counts them twice against the quota.
Preflight check
check is read-only: does used + reserved + planned_units cross the
limits?
snug meter check --metric docs4.tokens --planned-units 5000{
"allowed": false,
"current_usage": 620.0,
"reserved_units": 0.0,
"planned_units": 5000.0,
"soft_limit": 1000.0,
"soft_limit_exceeded": true,
"hard_limit": 2000.0,
"hard_limit_exceeded": true
}A failed check is still HTTP 200 with allowed: false - branch on the
body, not the status code. allowed only goes false when the hard limit
would be crossed (or quota is disabled - then it is always true).
Reserve, then commit
For work whose cost is unknown up front - an LLM call, a batch job - reserve the worst case, do the work, then commit what was actually used. Reserved units count against every check and reserve in the window, so concurrent callers cannot collectively overshoot the hard limit.
snug meter reserve --metric docs4.tokens --units 800{
"reservation_id": "rsv_8271726e-f98c-43a0-ba29-404f6b447968",
"reserved_units": 800.0,
"status": "reserved",
"expires_at": "2026-08-28T13:47:01.521774Z"
}snug meter commit --metric docs4.tokens \
--reservation-id rsv_8271726e-... --actual-units 500{
"reservation_id": "rsv_8271726e-f98c-43a0-ba29-404f6b447968",
"committed_units": 500.0,
"released_units": 300.0,
"status": "committed"
}Verified edges:
- Release is a commit with
--actual-units 0- all reserved units come back. There is no separate release endpoint. - An uncommitted reservation expires (default TTL 1 hour, see
expires_at); a background sweeper reclaims its units. - Committing twice is
400 invalid_value(reservation already committed). - Reserving past the hard limit is refused outright:
429 quota_exceeded(reserving 5000 exceeds hard limit 2000). - Another user's reservation id reads as
404 reservation_not_foundfor a non-admin, whether or not it exists.
Over-budget ingest
Ingest enforces the hard limit too, per event, atomically. An event that
would push used + reserved past the hard limit is not written:
{
"accepted": 0,
"duplicates": 0,
"rejected": 1,
"results": [
{ "idempotency_key": "req-791", "status": "rejected_quota", "bucket": null }
]
}The response is still 202 and the CLI exits 0 - a caller that ignores
results[].status will silently drop usage. Crossing only the soft limit
never rejects; it raises an alarm instead.
Alarms
Crossing a threshold during ingest records an alarm, deduplicated per
user scope, threshold, and quota window - it fires once, then again in the
next window. Captured live after ingest pushed used past the soft limit:
snug meter alarms --metric docs4.tokens{
"alarms": [
{
"alarm_id": "alarm_0b94cb0c-a854-4ae6-8b03-1653c0302c58",
"metric": "docs4.tokens",
"scope": "user:docs-wave",
"threshold": "soft_limit",
"usage": 1220.0,
"limit": 1000.0,
"status": "triggered",
"triggered_at": "2026-08-28T12:47:48.549665Z"
}
],
"count": 1
}Alarms are raised by the ingest path - a commit that crosses a threshold does not create one. The exact endpoint shapes are in the Meter API reference.