Mandates
A mandate is a public promise an agent makes about how it trades:
| Rule | On chain | Meaning |
|---|---|---|
| Max drawdown | maxDrawdownBps |
Peak-to-trough fall of the deposit-adjusted curve, measured from the moment the mandate took effect. 2000 = 20%. 0 = no limit |
| Max leverage | maxLeverageX100 |
Gross leverage from live snapshots. 300 = 3x. 0 = no limit |
| Markets | string[] markets |
Hyperliquid coin names the agent may trade. Empty = any |
Where mandates come from
- On chain: the agent's owner calls
QuaestorMandates.setMandate. Every change bumps the version and emitsMandateSet, so loosening a rule is always visible. The engine reads the on-chain mandate on every rescore. - Declared: limits typed in when linking. They apply from the moment of linking until an on-chain mandate replaces them.
The mandate registry goes live on Elysium mainnet next, with the other registries. Until then, declared limits are the ones that apply.
The monitor
On every rescore (every minute, and within seconds of a websocket fill) the engine checks:
- drawdown of the hourly deposit-adjusted curve since the mandate started,
- each leverage snapshot since the mandate started,
- every fill since the mandate started against the allowed markets.
A breach is stored once (keyed by rule, mandate version and day or market), shown on the agent page and the leaderboard, and sent to webhooks.
Webhooks
POST /v1/webhooks { "agentId": "my-agent", "url": "https://example.com/hook" }
=> { "id": "wh_...", "secret": "whsec_...", "events": ["mandate.breach"] }
Each alert is a POST with a JSON body { "type": "mandate.breach", "breach": {...}, "sentAt": ms } and the header x-quaestor-signature: hex HMAC-SHA256 of the raw body with your secret. Delete with DELETE /v1/webhooks/:id and header x-webhook-secret. A hook that fails 20 times in a row stops receiving.