Agent API
Version: 1.0
Date: 2026-07-11
Parent: docs/23-auto-yield-implementation.md
Plane: XEL-152 epic · overview page
Authority: docs/LOCKED-MODEL-2026-07-10.md
High-level orchestrator / agent skills over Open Yield.
Contracts hold funds; agents only instruct within owner policy.
0. XEL mapping (read first)
Section titled “0. XEL mapping (read first)”| Generic Open Yield name | XEL implementation |
|---|---|
positionId | characterId + shared endowmentObjectId (not multi-user vault shares) |
POST /open-yield/route | Discover + (if enabled) owner stake via adapters; deposit = top_up_endowment |
POST /open-yield/withdraw | withdraw_endowment_principal — owner only, never agent for full principal |
POST /open-yield/harvest | Permissionless: adapter claim_yield + treasury::heartbeat |
| Fee 5% in sketches | ~5% protocol_yield_skim_bps = 500 (configurable) |
| Base Solidity first | Sui first |
| Auto stake on route | OFF by default — route may deposit liquid only; stake needs Auto Yield enable |
Public path prefix (XEL product):
/api/characters/{character_id}/auto-yield/* and /api/open-yield/* (aliases).
Agent skill names (orchestrator tools) stay short: routeYield, harvest, withdraw, getPosition, enableAutoPay, previewRoute, rebalance.
1. Core high-level API calls
Section titled “1. Core high-level API calls”| # | Skill | HTTP | Purpose | MVP |
|---|---|---|---|---|
| 1 | routeYield | POST .../route | Deposit + optional intelligent stake plan | Yes |
| 2 | harvest | POST .../harvest | Claim yield, fee onchain, compound residual | Yes |
| 3 | withdraw | POST .../withdraw | Owner withdraw principal | Yes |
| 4 | getPosition | GET .../position | Liquid / staked / estimates / last harvest | Yes |
| 5 | enableAutoPay | POST .../auto-pay | Configure pay from post-fee residual | Yes |
| 6 | previewRoute | POST .../preview | Simulate route without broadcast | Yes |
| 7 | rebalance | POST .../rebalance | Move venue within allowlist | Phase 2 |
| 8 | listOpportunities | GET .../opportunities | Reputable pools (TVL ≥ $50M) | Yes (AY-12) |
2. Schemas
Section titled “2. Schemas”2.1 routeYield — POST /api/characters/{character_id}/open-yield/route
Section titled “2.1 routeYield — POST /api/characters/{character_id}/open-yield/route”Auth: owner or scoped agent (cannot withdraw principal).
{ "amount": 10000, "token": "USDC", "goal": "stable_agent_budget", "riskTolerance": "low", "preferredChains": ["sui"], "autoBridge": false, "autoCompound": true, "stake": false, "autoPayConfig": { "enabled": false, "percentage": 30, "targets": ["storage", "inference"] }}| Field | Required | Notes |
|---|---|---|
amount | yes | Human USDC units or micros — pick one contract; MVP = micros integer preferred |
token | yes | USDC only MVP |
goal | yes | Scoring hint only |
riskTolerance | no | low/medium/high — affects APY sanity / venue filter |
preferredChains | no | Discovery only; execute Sui unless policy expands |
autoBridge | no | If true and funds on Base/Sol, Mayan plan first |
autoCompound | no | Residual after harvest compounds (default true) |
stake | no | If true, requires Auto Yield enabled; else liquid deposit only |
autoPayConfig | no | Same as enableAutoPay |
Success:
{ "success": true, "characterId": "char_...", "endowmentObjectId": "0x...", "chain": "sui", "protocol": "suilend", "depositedAmountMicros": "10000000000", "staked": false, "estimates": { "grossApyBps": 381, "netApyBps": 343, "feeBps": 1000, "label": "estimate" }, "autoPayEnabled": false, "txDigest": null, "planStatus": "prepared"}2.2 harvest — POST /api/characters/{character_id}/open-yield/harvest
Section titled “2.2 harvest — POST /api/characters/{character_id}/open-yield/harvest”Auth: anyone (permissionless) for poke; gas/sponsor may apply.
{ "compound": true, "payPercentage": 0, "execute": false}Success:
{ "success": true, "grossYieldMicros": "245000000", "protocolFeeMicros": "24500000", "netYieldMicros": "220500000", "paidToAutoPayMicros": "0", "compoundedMicros": "220500000", "feeBps": 1000, "txDigest": null, "planStatus": "prepared"}Fee = onchain skim (~5%), not a second offchain fee.
2.3 withdraw — POST /api/characters/{character_id}/open-yield/withdraw
Section titled “2.3 withdraw — POST /api/characters/{character_id}/open-yield/withdraw”Auth: owner only (root). Agent returns UNAUTHORIZED.
{ "amountMicros": "5000000000", "token": "USDC", "unstakeFirst": true}Success:
{ "success": true, "withdrawnMicros": "5000000000", "liquidMicros": "...", "stakedMicros": "...", "txDigest": "..."}No fee on withdraw.
2.4 getPosition — GET /api/characters/{character_id}/open-yield/position
Section titled “2.4 getPosition — GET /api/characters/{character_id}/open-yield/position”{ "characterId": "char_...", "owner": "0x...", "token": "USDC", "chain": "sui", "protocol": "suilend", "liquidMicros": "1000000000", "stakedMicros": "9000000000", "currentValueMicros": "10000000000", "pendingYieldMicros": "187000000", "autoYieldEnabled": false, "autoPayEnabled": false, "autoPayPercentage": 0, "estimates": { "grossApyBps": 381, "netApyBps": 343, "estDailyYieldMicros": "...", "estMonthlyYieldMicros": "...", "runwayDays": null, "label": "estimate" }, "feeBps": 1000, "lastHarvestedAt": null, "createdAt": "..."}Missing APY → null, never demo 5%.
2.5 enableAutoPay — POST /api/characters/{character_id}/open-yield/auto-pay
Section titled “2.5 enableAutoPay — POST /api/characters/{character_id}/open-yield/auto-pay”Auth: owner (or agent with spend-policy scope).
{ "enabled": true, "percentage": 30, "targets": ["storage", "inference"], "frequency": "after_harvest"}Pays only from post-fee, post-survival residual. Never fan-credit liability.
2.6 previewRoute — POST /api/characters/{character_id}/open-yield/preview
Section titled “2.6 previewRoute — POST /api/characters/{character_id}/open-yield/preview”Same body as routeYield; never broadcasts. Returns scored venues, fees, bridge/gas estimates.
2.7 rebalance — Phase 2
Section titled “2.7 rebalance — Phase 2”Owner/agent within allowlist only; not free-form strategy bytes.
3. Error codes
Section titled “3. Error codes”| Code | HTTP | When |
|---|---|---|
INVALID_AMOUNT | 400 | amount ≤ 0 |
INSUFFICIENT_BALANCE | 400 | not enough liquid/stakeable |
POSITION_NOT_FOUND / CHARACTER_NOT_FOUND | 404 | unknown character |
UNAUTHORIZED | 403 | not owner/agent; agent trying principal withdraw |
AUTO_YIELD_DISABLED | 403 | stake requested but toggle off |
VENUE_NOT_ALLOWED | 400 | not in allowlist |
ADAPTER_NOT_READY | 503 | SDK/readiness fail |
HARVEST_FAILED | 500 | claim/heartbeat failed |
FEE_SINK_MISSING | 503 | cannot take protocol skim |
GAS_SPONSOR_MISSING | 503 | execute plan needs sponsor |
BRIDGE_NOT_ALLOWED | 400 | autoBridge true but policy forbids |
OPPORTUNITY_BELOW_TVL | 400 | below $50M reputation floor |
RATE_LIMIT | 429 | abuse |
4. Agent prompt templates
Section titled “4. Agent prompt templates”4.1 Route (liquid first)
Section titled “4.1 Route (liquid first)”You are the Open Yield orchestrator for an XEL character.
Goal: fund stable runway. Do NOT stake unless autoYieldEnabled is true.
1. getPosition(characterId)2. previewRoute({ amount, token: USDC, goal: stable_agent_budget, riskTolerance: low, stake: false })3. If owner confirmed, routeYield with stake:false unless policy says stake:true4. Report estimates.netApyBps and feeBps. Never invent APY.4.2 Harvest + optional Auto Pay
Section titled “4.2 Harvest + optional Auto Pay”Harvest character {characterId}.
1. harvest({ compound: true, payPercentage: 0, execute: false })2. Show grossYield, protocolFee (~5%), netYield3. Only execute=true if gas sponsor ready and policy allows4.3 Opportunity scan
Section titled “4.3 Opportunity scan”List Open Yield opportunities with TVL >= 50_000_000 USD, stablecoin, chains sui|base|ethereum|solana.Mark executable only if Sui adapter ready + allowlisted.Propose only — never stake.5. Deterministic tests (CI)
Section titled “5. Deterministic tests (CI)”Use fixed fixtures; no live mainnet required for unit tests. Fee default 500 bps (5%) in XEL fixtures (Open Yield product default).
5.1 Deposit / route
Section titled “5.1 Deposit / route”| ID | Case | Expected |
|---|---|---|
| DEP-001 | Valid deposit amount | top_up recorded, no fee |
| DEP-002 | amount 0 | INVALID_AMOUNT |
| DEP-003 | insufficient balance | INSUFFICIENT_BALANCE |
| DEP-004 | multiple top_ups | principal accumulates |
| ROUTE-001 | stake:true while Auto Yield off | AUTO_YIELD_DISABLED |
| ROUTE-002 | preview never broadcasts | no txDigest submitted |
| ROUTE-003 | autoBridge with Base USDC mock | Mayan plan in response |
5.2 Withdraw
Section titled “5.2 Withdraw”| ID | Case | Expected |
|---|---|---|
| WIT-001 | owner partial withdraw | amount out, no fee |
| WIT-002 | over liquid without unstake | fail or unstakeFirst path |
| WIT-003 | amount 0 | INVALID_AMOUNT |
| WIT-004 | agent withdraw principal | UNAUTHORIZED |
| WIT-005 | after harvest | value reflects residual compound |
5.3 Harvest + fee
Section titled “5.3 Harvest + fee”| ID | Case | Expected |
|---|---|---|
| HAR-001 | pending yield fixture 10000 | gross 10000 |
| HAR-002 | skim 5% | fee 500, net 9500 |
| HAR-003 | compound true | residual to principal |
| HAR-004 | non-owner harvest poke | allowed (permissionless) |
| HAR-005 | zero pending | no fee, no state change |
| FEE-001 | top_up 10000 | no skim |
| FEE-002 | withdraw | no fee |
| FEE-003 | fee sink missing | FEE_SINK_MISSING |
| FEE-004 | estimate surface | grossApy + netApy both present |
5.4 Auto Pay
Section titled “5.4 Auto Pay”| ID | Case | Expected |
|---|---|---|
| PAY-001 | enable valid targets | saved |
| PAY-002 | harvest payPercentage 30 | ≤ 30% of post-fee residual |
| PAY-003 | percentage 0 | no pay |
| PAY-004 | invalid target | error |
| PAY-005 | fan credit liability | never paid from |
5.5 Opportunities / reputation
Section titled “5.5 Opportunities / reputation”| ID | Case | Expected |
|---|---|---|
| OPP-001 | TVL 49.9M | excluded |
| OPP-002 | TVL 50M | included |
| OPP-003 | non-stablecoin | excluded MVP |
| OPP-004 | new vs snapshot | one new event |
| OPP-005 | no adapter | watch_only |
| OPP-006 | agent never supply without enable | assert |
5.6 Bridge / swap / gas
Section titled “5.6 Bridge / swap / gas”| ID | Case | Expected |
|---|---|---|
| BR-001 | Mayan quote mock | normalized |
| BR-002 | dest not Sui | blocked |
| BR-003 | execution off | prepared only |
| SW-001 | pool not allowlisted | blocked |
| GAS-001 | sponsor missing | plan blocked |
| GAS-002 | overage stays character | not markup |
5.7 Security / access
Section titled “5.7 Security / access”| ID | Case | Expected |
|---|---|---|
| SEC-001 | agent cannot withdraw principal | UNAUTHORIZED |
| SEC-002 | agent cannot widen fee bps | blocked |
| SEC-003 | rebalance non-allowlisted venue | blocked |
| SEC-004 | secrets redacted in responses | no keypair fields |
| SEC-005 | idempotent harvest poke | single effect |
5.8 E2E (integration, still deterministic with mocks)
Section titled “5.8 E2E (integration, still deterministic with mocks)”| ID | Flow |
|---|---|
| E2E-001 | top_up → enable Auto Yield → stake mock → harvest fee → compound |
| E2E-002 | top_up → harvest 0 → withdraw |
| E2E-003 | enable Auto Pay → harvest → pay storage mock |
| E2E-004 | Base USDC mock → Mayan plan → credit → getPosition |
| E2E-005 | opportunity agent propose only (no stake) |
6. Fixed CI test data
Section titled “6. Fixed CI test data”| Variable | Value |
|---|---|
| Deposit | 10_000_000_000 micros (10_000 USDC) |
| Fee | 500 bps (5%) |
| Auto Pay % | 30 |
| Harvest gross fixture | 245_000_000 micros |
| Expected fee | 12_250_000 |
| Expected net | 232_750_000 |
| Min TVL | 50_000_000 USD |
| Default venue | suilend |
7. Suggested test layout (XEL repo)
Section titled “7. Suggested test layout (XEL repo)”tests/ open-yield-route.test.mjs open-yield-harvest-fee.test.mjs open-yield-withdraw-auth.test.mjs open-yield-auto-pay.test.mjs open-yield-opportunities.test.mjs open-yield-bridge-gas.test.mjs backend_open_yield_api_test.pycontracts/v2/tests/ # fee waterfall already partialMap to tickets: AY-01…16 (XEL-153…168) — each ticket already has its T1…Tn; this doc is the union master list.
8. MVP must-have skills only
Section titled “8. MVP must-have skills only”previewRouterouteYield(deposit ± opt-in stake)getPositionharvest(fee onchain)withdraw(owner)enableAutoPaylistOpportunities
rebalance = Phase 2.