Skip to content

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.


Generic Open Yield nameXEL implementation
positionIdcharacterId + shared endowmentObjectId (not multi-user vault shares)
POST /open-yield/routeDiscover + (if enabled) owner stake via adapters; deposit = top_up_endowment
POST /open-yield/withdrawwithdraw_endowment_principalowner only, never agent for full principal
POST /open-yield/harvestPermissionless: adapter claim_yield + treasury::heartbeat
Fee 5% in sketches~5% protocol_yield_skim_bps = 500 (configurable)
Base Solidity firstSui first
Auto stake on routeOFF 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.


#SkillHTTPPurposeMVP
1routeYieldPOST .../routeDeposit + optional intelligent stake planYes
2harvestPOST .../harvestClaim yield, fee onchain, compound residualYes
3withdrawPOST .../withdrawOwner withdraw principalYes
4getPositionGET .../positionLiquid / staked / estimates / last harvestYes
5enableAutoPayPOST .../auto-payConfigure pay from post-fee residualYes
6previewRoutePOST .../previewSimulate route without broadcastYes
7rebalancePOST .../rebalanceMove venue within allowlistPhase 2
8listOpportunitiesGET .../opportunitiesReputable pools (TVL ≥ $50M)Yes (AY-12)

2.1 routeYieldPOST /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"]
}
}
FieldRequiredNotes
amountyesHuman USDC units or micros — pick one contract; MVP = micros integer preferred
tokenyesUSDC only MVP
goalyesScoring hint only
riskTolerancenolow/medium/high — affects APY sanity / venue filter
preferredChainsnoDiscovery only; execute Sui unless policy expands
autoBridgenoIf true and funds on Base/Sol, Mayan plan first
autoCompoundnoResidual after harvest compounds (default true)
stakenoIf true, requires Auto Yield enabled; else liquid deposit only
autoPayConfignoSame 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 harvestPOST /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 withdrawPOST /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 getPositionGET /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 enableAutoPayPOST /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 previewRoutePOST /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.


Owner/agent within allowlist only; not free-form strategy bytes.


CodeHTTPWhen
INVALID_AMOUNT400amount ≤ 0
INSUFFICIENT_BALANCE400not enough liquid/stakeable
POSITION_NOT_FOUND / CHARACTER_NOT_FOUND404unknown character
UNAUTHORIZED403not owner/agent; agent trying principal withdraw
AUTO_YIELD_DISABLED403stake requested but toggle off
VENUE_NOT_ALLOWED400not in allowlist
ADAPTER_NOT_READY503SDK/readiness fail
HARVEST_FAILED500claim/heartbeat failed
FEE_SINK_MISSING503cannot take protocol skim
GAS_SPONSOR_MISSING503execute plan needs sponsor
BRIDGE_NOT_ALLOWED400autoBridge true but policy forbids
OPPORTUNITY_BELOW_TVL400below $50M reputation floor
RATE_LIMIT429abuse

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:true
4. Report estimates.netApyBps and feeBps. Never invent APY.
Harvest character {characterId}.
1. harvest({ compound: true, payPercentage: 0, execute: false })
2. Show grossYield, protocolFee (~5%), netYield
3. Only execute=true if gas sponsor ready and policy allows
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.

Use fixed fixtures; no live mainnet required for unit tests. Fee default 500 bps (5%) in XEL fixtures (Open Yield product default).

IDCaseExpected
DEP-001Valid deposit amounttop_up recorded, no fee
DEP-002amount 0INVALID_AMOUNT
DEP-003insufficient balanceINSUFFICIENT_BALANCE
DEP-004multiple top_upsprincipal accumulates
ROUTE-001stake:true while Auto Yield offAUTO_YIELD_DISABLED
ROUTE-002preview never broadcastsno txDigest submitted
ROUTE-003autoBridge with Base USDC mockMayan plan in response
IDCaseExpected
WIT-001owner partial withdrawamount out, no fee
WIT-002over liquid without unstakefail or unstakeFirst path
WIT-003amount 0INVALID_AMOUNT
WIT-004agent withdraw principalUNAUTHORIZED
WIT-005after harvestvalue reflects residual compound
IDCaseExpected
HAR-001pending yield fixture 10000gross 10000
HAR-002skim 5%fee 500, net 9500
HAR-003compound trueresidual to principal
HAR-004non-owner harvest pokeallowed (permissionless)
HAR-005zero pendingno fee, no state change
FEE-001top_up 10000no skim
FEE-002withdrawno fee
FEE-003fee sink missingFEE_SINK_MISSING
FEE-004estimate surfacegrossApy + netApy both present
IDCaseExpected
PAY-001enable valid targetssaved
PAY-002harvest payPercentage 30≤ 30% of post-fee residual
PAY-003percentage 0no pay
PAY-004invalid targeterror
PAY-005fan credit liabilitynever paid from
IDCaseExpected
OPP-001TVL 49.9Mexcluded
OPP-002TVL 50Mincluded
OPP-003non-stablecoinexcluded MVP
OPP-004new vs snapshotone new event
OPP-005no adapterwatch_only
OPP-006agent never supply without enableassert
IDCaseExpected
BR-001Mayan quote mocknormalized
BR-002dest not Suiblocked
BR-003execution offprepared only
SW-001pool not allowlistedblocked
GAS-001sponsor missingplan blocked
GAS-002overage stays characternot markup
IDCaseExpected
SEC-001agent cannot withdraw principalUNAUTHORIZED
SEC-002agent cannot widen fee bpsblocked
SEC-003rebalance non-allowlisted venueblocked
SEC-004secrets redacted in responsesno keypair fields
SEC-005idempotent harvest pokesingle effect

5.8 E2E (integration, still deterministic with mocks)

Section titled “5.8 E2E (integration, still deterministic with mocks)”
IDFlow
E2E-001top_up → enable Auto Yield → stake mock → harvest fee → compound
E2E-002top_up → harvest 0 → withdraw
E2E-003enable Auto Pay → harvest → pay storage mock
E2E-004Base USDC mock → Mayan plan → credit → getPosition
E2E-005opportunity agent propose only (no stake)

VariableValue
Deposit10_000_000_000 micros (10_000 USDC)
Fee500 bps (5%)
Auto Pay %30
Harvest gross fixture245_000_000 micros
Expected fee12_250_000
Expected net232_750_000
Min TVL50_000_000 USD
Default venuesuilend

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.py
contracts/v2/tests/ # fee waterfall already partial

Map to tickets: AY-01…16 (XEL-153…168) — each ticket already has its T1…Tn; this doc is the union master list.


  1. previewRoute
  2. routeYield (deposit ± opt-in stake)
  3. getPosition
  4. harvest (fee onchain)
  5. withdraw (owner)
  6. enableAutoPay
  7. listOpportunities

rebalance = Phase 2.