In this section: Quotas and refusals

Quotas and refusals

The four daily ceilings, when they reset, what a refusal looks like on the wire, and how to tell a spent ceiling from a scope or Membership refusal.

Four ceilings

All four are per user per day, checked together, so a call refused by a tighter ceiling never leaves a looser one already spent.

CeilingValueCounts
knowledge_search50 callsThat one tool
All calls500Every call, read or write
Writes100Any call to a write tool. Reads keep working when this is spent.
Changed rows1,000Charged by real batch size: a call that changes 50 records charges 50

Every ceiling resets at midnight Pacific, not UTC. The numbers are a guard against a runaway loop, set far above any real session; they are not a pricing tier.

What a refusal looks like

A refusal is flagged, not only worded. It arrives as a tool result with isError: true; the text is a sentence written for a person, and structuredContent.result carries the same message under a code for a client that branches rather than reads.

{ "isError": true, "content": [{"type": "text", "text": "You've hit today's limit for changes to your Miles wallet (100 writes). It resets at midnight Pacific. Reading your wallet still works, so you can keep asking questions, just not recording answers until then."}], "structuredContent": {"result": {"error": {"code": "quota_exceeded", "message": "You've hit today's limit for changes to your Miles wallet (100 writes). …"}}} }

Every refusal names the ceiling, the reset in a concrete timezone, and what still works. A batch refusal says how many records it would have changed and how many remain, and confirms nothing was written.

Three refusals that look alike

CodeMeansResetsFix
quota_exceededA daily ceiling is spentMidnight PacificWait, or send a smaller batch
missing scopeThe token lacks miles:writeNever on its ownThe user re-approves with the write toggle on
supporter_requiredThe tool needs the user to be a MemberNeverThe user joins Membership; the code keeps its historical spelling

A client that treats all three as "try again later" will loop on the second and third forever. Branch on the code.

The advisory before the refusal

Past 80% of any ceiling, a successful result carries an advisory string alongside its data: 410 of today's 500 Miles calls used. It resets at midnight Pacific. It is the only channel Miles has into a client's UI before the refusal itself, so surface it.

What this is not

A daily ceiling is not rate limiting. It does nothing about a burst, an oversized request, or concurrency; the registration and token endpoints have their own per-IP limit of 20 requests a minute.