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.
| Ceiling | Value | Counts |
|---|---|---|
knowledge_search | 50 calls | That one tool |
| All calls | 500 | Every call, read or write |
| Writes | 100 | Any call to a write tool. Reads keep working when this is spent. |
| Changed rows | 1,000 | Charged 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
| Code | Means | Resets | Fix |
|---|---|---|---|
quota_exceeded | A daily ceiling is spent | Midnight Pacific | Wait, or send a smaller batch |
| missing scope | The token lacks miles:write | Never on its own | The user re-approves with the write toggle on |
supporter_required | The tool needs the user to be a Member | Never | The 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.