Quota enforcement
Every chargeable action on the agent side is wrapped in a consume call.
Happy path (within quota)
Quota exhausted
The marketplace never half-writes on overage — the transaction rolls back if newUsed > effectiveQuota.
No active subscription
The agent should bounce the user to subscribe, not crash.
Release (fixed metrics only)
Release on a rolling metric returns 422 — rolling counters never decrement.
Idempotency
clientReqId is the agent's responsibility. Always generate a UUID per logical attempt. If the request fails with a network error, retry with the same clientReqId. The marketplace's usage_ledger.clientReqId UNIQUE ensures the second write is a no-op and the response mirrors the first successful state.
// idempotent retry loop on agent side
let attempt = 0;
while (true) {
try {
const r = await fetch(consumeUrl, {
method: "POST",
body: JSON.stringify({ delta: 1, clientReqId }),
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
});
return r;
} catch (e) {
if (++attempt > 3) throw e;
await sleep(300 * attempt);
}
}
Reading current usage
GET /me/agents/:agentId/usage returns all metrics. GET /me/agents/:agentId/usage/:metricSlug returns one.
Both return the same envelope:
type MetricUsageDto = {
metricSlug: string;
displayName: string;
unit: string;
kind: "fixed" | "rolling";
baseQuota: number | null;
addonsBonus: number;
effectiveQuota: number | null; // null = unlimited
used: number;
remaining: number | null; // null = unlimited
resetsAt: string | null; // null for fixed; period end for rolling
};
Use this for dashboard widgets, not for permission checks (a check that uses cached data can let through over-budget calls). Always wrap chargeable actions in consume.
Period reset
When currentPeriodEnd passes and a rolling consume comes in, the handler zeros subscription_usage.used before incrementing. No cron, no separate job.