name: milo version: 1.0.0 description: Autonomous Solana portfolio management. Non-custodial wallets, auto-trading, market analysis, orders, transfers, and AI conversations.
Milo Partner API Skill
Milo is an autonomous Solana portfolio manager. Through this API you can register users, create non-custodial wallets, send tokens, place buy/sell orders, manage positions, configure auto-trading strategies, and converse with Milo's AI agents.
Getting Started — First-Time Onboarding
Follow these 4 steps to go from zero to a fully active Milo account.
Step 1: Sign up
Request a SIWX message, sign it with your Solana wallet, and register.
# 1a. Get the message to sign
curl -X POST {{BASE_URL}}/api/v1/users/siwx/message \
-H "Content-Type: application/json" \
-d '{
"accountAddress": "<your-wallet-address>",
"chainId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
}'
# 1b. Sign the returned "message" (UTF-8 bytes) with your ed25519 key, base58-encode the signature
# 1c. Register
curl -X POST {{BASE_URL}}/api/v1/users \
-H "Content-Type: application/json" \
-d '{
"signupWallet": "<your-wallet-address>",
"siwx": {
"data": <data-object-from-1a>,
"message": "<message-string-from-1a>",
"signature": "<base58-signature-from-1b>"
}
}'
# → returns apiKey, user.id, wallets[] (save these to ~/.milo/config.json)
Step 2: Ask Milo what's trending
Start a conversation with the market analyst to explore current opportunities.
# 2a. Create a conversation
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/conversations \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "What tokens are trending right now on Solana?",
"agentType": "market-analyst"
}'
# → returns conversationId
# 2b. Poll for the response (wait 2-3s between polls)
curl "{{BASE_URL}}/api/v1/users/{userId}/conversations/{conversationId}/messages" \
-H "X-API-Key: $API_KEY"
# → when processing: false, the agent has responded
Step 3: Transfer funds to your Milo wallet
Deposit SOL to your Milo wallet so it has capital to trade. The Milo wallet address is wallets[1].address from signup (the type: "milo" wallet).
Send SOL from your external wallet to that address. Once funded, verify:
curl {{BASE_URL}}/api/v1/wallets/{walletId}/holdings \
-H "X-API-Key: $API_KEY"
Step 4: Activate auto-trading
Turn on Milo's autonomous trading agent. It will start scanning for opportunities within ~30 minutes.
curl -X PATCH {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"isActive": true,
"riskTolerance": "balanced",
"strategy": "SWING TRADER"
}'
That's it — Milo is now managing your portfolio. Read on for the full feature guide and API reference.
Feature Overview
Note: You do NOT need to read this entire document. Use the guide below to jump to the relevant section for your task.
| If you need to... | Read section |
|---|---|
| Look up your user ID and wallet IDs | "Me" (under Complete API Reference) |
| Register a new user / get an API key | "First Boot Protocol" and "Signup" (under Complete API Reference) |
| Activate or configure auto-trading | "Recipe 2: Activate the auto-trader" and "Auto-Trade Settings" (under Complete API Reference) |
| Create, link, or manage strategies | "Strategies" (under Complete API Reference) |
| Deploy and manage arena strategies | "Arena" (under Complete API Reference) |
| View quests and claim bones | "Quests & Bones" (under Complete API Reference) |
| Place, list, or manage orders | "Orders" (under Complete API Reference) |
| Send tokens to another wallet | "Wallet Actions" (under Complete API Reference) |
| Check positions, holdings, or PnL | "Recipe 4: Fetch your positions" and "Portfolio" (under Complete API Reference) |
| Chat with Milo's AI agents | "Recipe 3: Talk with Milo" / "Recipe 6: Ask about a token" and "Conversations" (under Complete API Reference) |
| Understand the full onboarding flow | "Quick Start" and "First Boot Protocol" |
| Set up a recurring portfolio check-in | "Heartbeat Protocol" |
Capabilities:
- Non-custodial Solana wallet — Each user gets a Milo wallet (created via Turnkey). The user's signing key owns the wallet; Milo receives delegated permission to execute trades.
- Auto-trading — Configure risk tolerance, strategy, and asset allocation. Milo's auto-trader agent monitors markets and executes trades autonomously.
- Orders — Create limit, market, stop-loss, and take-profit orders on any Solana token.
- Token transfers — Send any SPL token or SOL from your Milo wallet to any Solana address.
- Positions — Track open positions with PnL data, close positions.
- Arena — Deploy public strategies to a competitive leaderboard. Milo funds an arena wallet and trades autonomously using the strategy. Withdraw to reclaim holdings.
- Quests & Bones — Complete quests (event-driven tasks like trades, signups) to earn bones (reward points). Check quests regularly and claim bones for completed quests.
- AI conversations — Chat with Milo's market analyst, auto-trader, or game agent. Async processing with polling.
- Portfolio data — Holdings, transactions, executed transactions, diary logs.
Quick Start
- Get SIWX message - Call
POST /api/v1/users/siwx/messagewith your wallet address to get a message to sign. - Register - Sign the message with your wallet's ed25519 key, then call
POST /api/v1/userswith the signed SIWX proof to create a user and receive an API key + Milo wallet. - Save credentials - Store the API key, user ID, wallet ID, and wallet address in
~/.milo/config.json. - Deposit SOL - Send SOL to your Milo wallet address (the
type: "milo"wallet from signup). - Activate auto-trader - Call
PATCH /api/v1/users/{userId}/auto-trade-settingswith{ "isActive": true }. - Fetch open quests - Call
GET /api/v1/users/{userId}/queststo see available quests. Claim bones for any completed quests. - Start trading - Create orders, chat with agents, or let the auto-trader manage your portfolio.
Credential Storage
After signup, persist credentials locally so they survive across sessions:
// ~/.milo/config.json
{
"api_key": "mk_live_...",
"user_id": "uuid",
"wallet_id": "uuid",
"wallet_address": "7xKXtg...",
"base_url": "{{BASE_URL}}"
}
Warning: In ephemeral environments (containers, serverless), this file may not persist. Use environment variables or a secrets manager instead.
All authenticated requests require the header:
X-API-Key: <api_key>
(NOT Authorization: Bearer — the API uses the X-API-Key header.)
MCP Access
The partner API server exposes MCP on the same host at /mcp using Streamable HTTP:
POST /mcpfor initialize and JSON-RPC requestsGET /mcpfor SSE streamDELETE /mcpfor session termination
POST /mcp initialize accepts optional X-API-Key. If omitted, the MCP session starts unauthenticated and only signup/public tools are usable until signup returns an API key. Request throttling and session-capacity protections are enforced, so initialize may return 429. On 429, respect Retry-After and retry after the indicated delay. Sessions can also expire, so re-initialize after invalid-session errors.
For sessioned requests after initialize, send Mcp-Session-Id.
Wallet & Deposits
Milo creates a non-custodial Solana wallet for each user via Turnkey. The wallet-creating account is the owner; Milo receives delegated permission for trading.
On signup you receive two wallets:
type: "signup"- Your external signing wallet (used for SIWX verification).type: "milo"- Your Milo trading wallet. Deposit SOL here for trading.
To check your wallet balance, use the holdings endpoint:
curl {{BASE_URL}}/api/v1/wallets/{walletId}/holdings \
-H "X-API-Key: $API_KEY"
First Boot Protocol
When starting fresh with no saved credentials:
- Generate a Solana keypair for SIWX signing.
- Request a SIWX message from the server:
This returnscurl -X POST {{BASE_URL}}/api/v1/users/siwx/message \ -H "Content-Type: application/json" \ -d '{ "accountAddress": "<your-wallet-address>", "chainId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" }'{ "data": { ... }, "message": "<message-string>" }. Thedataobject contains all SIWX fields (domain, uri, nonce, etc.) and themessageis the human-readable string to sign. - Sign the message — Sign the
messagestring (UTF-8 encoded bytes) with your Solana keypair's ed25519 private key. Base58-encode the resulting signature. - Register:
Important: Pass thecurl -X POST {{BASE_URL}}/api/v1/users \ -H "Content-Type: application/json" \ -d '{ "signupWallet": "<your-wallet-address>", "siwx": { "data": <data-object-from-step-2>, "message": "<message-string-from-step-2>", "signature": "<base58-signature-from-step-3>" } }'dataandmessageexactly as returned by the server. Do not construct them manually. - Save the returned
apiKey,user.id, andwallets[1](the Milo wallet) to~/.milo/config.json. - Deposit SOL to the Milo wallet address.
- Configure auto-trade:
curl -X PATCH {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings \ -H "X-API-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "isActive": true, "riskTolerance": "balanced", "strategy": "SWING TRADER" }'
Heartbeat Protocol
See heartbeat.md for the recurring check-in protocol. Run every 4+ hours to stay informed about your portfolio.
Cookbook — Working with Milo
This cookbook walks through the most common workflows end-to-end. Follow these recipes to get up and running quickly.
Recipe 1: Get your API key
Before anything else you need credentials. Generate a Solana keypair, request a SIWX message from the server, sign it, and register.
# 1. Request a SIWX message to sign
curl -X POST {{BASE_URL}}/api/v1/users/siwx/message \
-H "Content-Type: application/json" \
-d '{
"accountAddress": "<your-wallet-address>",
"chainId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
}'
# → returns { "data": { "data": { ... }, "message": "<message-to-sign>" } }
# 2. Sign the "message" string (UTF-8 bytes) with your ed25519 private key
# Base58-encode the signature
# 3. Register with the signed proof
curl -X POST {{BASE_URL}}/api/v1/users \
-H "Content-Type: application/json" \
-d '{
"signupWallet": "<your-wallet-address>",
"siwx": {
"data": <data-object-from-step-1>,
"message": "<message-string-from-step-1>",
"signature": "<base58-signature-from-step-2>"
}
}'
# 4. Save the response — you need these for every subsequent call
# api_key → Authorization header
# user.id → {userId} in routes
# wallets[1].id → {walletId} (the "milo" wallet)
# wallets[1].address → deposit SOL here
Store credentials in ~/.milo/config.json so they persist across sessions.
Recipe 2: Activate the auto-trader
Once registered, turn on Milo's autonomous trading agent.
curl -X PATCH {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"isActive": true,
"riskTolerance": "balanced",
"strategy": "SWING TRADER",
"instructions": "Focus on SOL ecosystem tokens. Avoid meme coins.",
"customTickers": ["SOL", "JUP", "BONK"]
}'
That's it — Milo will start monitoring markets and placing trades according to your configuration. You can verify it's active:
curl {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings \
-H "X-API-Key: $API_KEY"
Recipe 3: Talk with Milo about your investment strategy
Use the auto-trader agent to discuss, refine, or brainstorm your trading strategy. This is a conversation — Milo understands your portfolio context.
# 1. Start a conversation with the auto-trader
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/conversations \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "I want to shift my strategy toward DeFi blue-chips. What allocation do you recommend for a balanced risk profile?",
"agentType": "auto-trader"
}'
# → returns { conversationId, processing: true }
# 2. Poll for the response (wait 2-3s between polls)
curl "{{BASE_URL}}/api/v1/users/{userId}/conversations/{conversationId}/messages" \
-H "X-API-Key: $API_KEY"
# → when processing: false, the agent has responded
# 3. Continue the conversation
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/conversations/{conversationId}/messages \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "message": "Good plan. Apply those changes to my auto-trade settings." }'
Tip: The auto-trader agent can directly update your settings when you ask it to — it has tool access to modify your allocation, risk tolerance, and instructions.
Recipe 4: Fetch your positions
Check what Milo is holding and how each position is performing.
# List all active positions with PnL
curl "{{BASE_URL}}/api/v1/users/{userId}/positions?status=active&page=1&pageSize=50" \
-H "X-API-Key: $API_KEY"
Each position includes the token, entry price, current PnL, and thesis. Use status=pending for positions still being built, or omit the filter to get everything.
For a deeper look at your wallet's token balances:
curl {{BASE_URL}}/api/v1/wallets/{walletId}/holdings \
-H "X-API-Key: $API_KEY"
Recipe 5: Revalidate a position with Milo
Have Milo re-analyze an existing position to decide whether to hold, add, or exit.
# 1. Get the position's thesisId from the positions list (Recipe 4)
# 2. Start a conversation with the auto-trader, referencing the position
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/conversations \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "Re-evaluate my position on JUP (thesis {thesisId}). Given the latest market conditions, should I hold, take partial profit, or close it entirely?",
"agentType": "auto-trader"
}'
# 3. Poll for the response
curl "{{BASE_URL}}/api/v1/users/{userId}/conversations/{conversationId}/messages" \
-H "X-API-Key: $API_KEY"
# 4. If Milo recommends closing, you can close it directly:
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/positions/{thesisId}/close \
-H "X-API-Key: $API_KEY"
Recipe 6: Ask Milo about a specific token
Use the market-analyst agent for token research. It can pull market data, analyze trends, and give you a thesis.
# 1. Ask the market analyst about a token
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/conversations \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "What do you think about RENDER? Give me a full analysis — fundamentals, technicals, and whether it fits my current portfolio.",
"agentType": "market-analyst"
}'
# 2. Poll for the response
curl "{{BASE_URL}}/api/v1/users/{userId}/conversations/{conversationId}/messages" \
-H "X-API-Key: $API_KEY"
# 3. Follow up with more questions in the same conversation
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/conversations/{conversationId}/messages \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "message": "Compare it with RNDR vs HNT for a DePIN play. Which is the better entry right now?" }'
Tip: The market-analyst agent focuses on research and analysis. If you want Milo to actually execute a trade based on the analysis, either create an order manually or switch to the auto-trader agent.
Putting it all together
A typical session with Milo looks like:
- Check in — Fetch positions and holdings (Recipes 4)
- Strategize — Talk to the auto-trader about adjusting your approach (Recipe 3)
- Research — Ask the market analyst about tokens you're curious about (Recipe 6)
- Validate — Revalidate existing positions with fresh analysis (Recipe 5)
- Act — Let the auto-trader handle execution, or place manual orders via the Orders API
Run the heartbeat protocol every 4+ hours to keep this cycle going automatically.
Complete API Reference
Authentication
All endpoints (except signup) require:
X-API-Key: <api_key>
Me
GET /api/v1/me
Get the current user profile and wallets for the authenticated API key. Use this to discover your userId and walletId values.
curl {{BASE_URL}}/api/v1/me \
-H "X-API-Key: $API_KEY"
Response (200):
{
"user": {
"id": "uuid",
"signupWalletId": "uuid",
"provider": "siwx",
"createdAt": "2025-01-01T00:00:00.000Z"
},
"wallets": [
{ "id": "uuid", "address": "<address>", "chain": "solana", "type": "signup" },
{ "id": "uuid", "address": "<address>", "chain": "solana", "type": "milo" }
]
}
Signup
Signup is a two-step process: first request a SIWX message from the server, then sign it and submit the proof.
POST /api/v1/users/siwx/message
Generate a SIWX message for the wallet to sign. This is step 1 of signup.
curl -X POST {{BASE_URL}}/api/v1/users/siwx/message \
-H "Content-Type: application/json" \
-d '{
"accountAddress": "<wallet-address>",
"chainId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
}'
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
accountAddress |
string | yes | Solana wallet address (32-64 chars) |
chainId |
string | yes | CAIP-2 chain ID (e.g. solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp) |
inviteCode |
string | no | Optional invite code |
Response (200):
{
"data": {
"data": {
"accountAddress": "<wallet-address>",
"chainId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
"domain": "andmilo.com",
"uri": "https://andmilo.com",
"version": "v1",
"nonce": "<server-generated-nonce>",
"issuedAt": "2025-06-15T12:00:00.000Z",
"expirationTime": "2025-06-22T12:00:00.000Z",
"statement": "By signing, you agree to andmilo Terms of Use..."
},
"message": "andmilo.com wants you to sign in with your Solana account:\n<wallet-address>\n\n..."
}
}
Sign the message string (UTF-8 encoded bytes) with the wallet's ed25519 private key. Base58-encode the signature. Then proceed to step 2.
POST /api/v1/users
Create a new user via SIWX wallet verification. This is step 2 of signup.
curl -X POST {{BASE_URL}}/api/v1/users \
-H "Content-Type: application/json" \
-d '{
"signupWallet": "<wallet-address>",
"siwx": {
"data": <data-object-from-siwx-message-response>,
"message": "<message-string-from-siwx-message-response>",
"signature": "<base58-ed25519-signature>"
}
}'
Important: Pass the data and message fields exactly as returned by POST /api/v1/users/siwx/message. Do not construct them manually.
Response (200):
{
"data": {
"user": { "id": "uuid", "signupWalletId": "uuid", "provider": "siwx", "createdAt": "..." },
"wallets": [
{ "id": "uuid", "address": "<address>", "chain": "solana", "type": "signup" },
{ "id": "uuid", "address": "<address>", "chain": "solana", "type": "milo" }
],
"apiKey": "mk_live_..."
}
}
Auto-Trade Settings
GET /api/v1/users/{userId}/auto-trade-settings
Get current auto-trade configuration.
curl {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings \
-H "X-API-Key: $API_KEY"
PATCH /api/v1/users/{userId}/auto-trade-settings
Update auto-trade configuration.
curl -X PATCH {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"isActive": true,
"riskTolerance": "balanced",
"strategy": "SWING TRADER",
"instructions": "Focus on SOL ecosystem tokens",
"customTickers": ["SOL", "JUP", "BONK"],
"dataSources": { "fundingRates": true, "openInterest": true },
"assetClassSettings": {
"memes": { "dataSources": { "liquidationData": true } },
"majors": { "dataSources": { "macroData": true } }
}
}'
Settings fields:
| Field | Type | Values |
|---|---|---|
isActive |
boolean | Enable/disable auto-trading |
riskTolerance |
string | conservative, balanced, degen |
strategy |
string | VALUE INVESTOR, SWING TRADER, SCALPER, CUSTOM |
strategyId |
uuid | null | Link a saved strategy to auto-trade settings |
modelVersion |
string | null | Preferred model |
instructions |
string | Free-text trading instructions for the agent |
customTickers |
string[] | Specific tokens to focus on |
allocation |
object | Asset class allocation percentages |
dataSources |
object | null | Global data-source toggles: fundingRates, openInterest, liquidationData, macroData |
assetClassSettings |
object | null | Per-asset-class configuration, including nested dataSources overrides |
Asset classes for allocation: trenches, memes, promising-memes, staking, native, majors, stables, xStocks, custom
Data-source resolution notes:
- Asset-class
dataSourcesoverrides win over top-leveldataSources. - If no override exists, the top-level value applies.
- Missing keys are treated as disabled.
- PATCH deep-merges
dataSourcesboth globally and insideassetClassSettings, so partial updates do not wipe sibling keys.
Model entitlement notes:
- Only canonical model ids are accepted on the partner surface.
- Canonical OpenAI model ids are
o3,gpt-5.2-high,gpt-5.2-xh, andgpt-5.4. - Canonical Anthropic model ids are
claude-opus-4.5andclaude-opus-4.6. - Canonical Gemini model ids are
gemini-3-proandgemini-3.1-pro-preview. - Canonical Grok model ids are
grok-4.1-fast-reasoningandgrok-4. - If a model is unavailable for your account, the API returns
400 Bad Requestwitherror.details.requiredPlan,error.details.upgradeUrl, and an error message containing the same plan-specific Stripe link.
Data-source entitlement notes:
- Only
proandmaxusers can create, update, apply, sync, or otherwise changedataSources. - Free users can still read saved
dataSourcesin GET responses. - If an account downgrades, saved
dataSourcesremain visible but are inactive in auto-trader chat, execution, and position review until the account is back on Pro or Max. - When blocked, the API returns
400 Bad Requestwitherror.details.feature = "dataSources",error.details.requiredPlan = "pro", and upgrade details when available.
Strategies
Strategies are reusable autotrade configurations that live under auto-trade settings. Create a strategy, link it to your settings, and sync when the strategy evolves.
Workflow:
- Create a strategy with allocation, instructions, and trading approach.
- Link it via
PATCH /auto-trade-settingswith{ "strategyId": "<uuid>" }. This takes a snapshot of the strategy into your settings. - Use — Milo auto-trades according to the snapshot.
- If the strategy is updated later, the GET settings response includes
strategySync.synced: false. - Sync — call
POST .../strategies/{strategyId}/syncto re-apply the latest version.
POST /api/v1/users/{userId}/auto-trade-settings/strategies
Create a new strategy.
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings/strategies \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "SOL Ecosystem DCA",
"description": "Dollar cost average into SOL ecosystem tokens",
"strategy": "SWING TRADER",
"instructions": "Focus on SOL, JUP, and BONK with balanced entries",
"allocation": { "majors": 45, "native": 25, "staking": 10, "promising-memes": 15, "xStocks": 5 },
"customTickers": ["SOL", "JUP", "BONK"],
"dataSources": { "fundingRates": true, "openInterest": true },
"assetClassSettings": {
"memes": { "dataSources": { "liquidationData": true } },
"majors": { "dataSources": { "macroData": true } }
},
"isPublic": false
}'
Response (201):
{
"data": {
"id": "uuid",
"name": "SOL Ecosystem DCA",
"strategy": "SWING TRADER",
"allocation": { ... },
"createdAt": "..."
}
}
Create/update fields:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes (create) | Strategy name (1-200 chars) |
description |
string | null | no | Description (max 2000 chars) |
instructions |
string | no | Free-text instructions (max 4000 chars) |
strategy |
string | yes (create) | VALUE INVESTOR, SWING TRADER, SCALPER, CUSTOM |
allocation |
object | no | Asset class allocation percentages |
customTickers |
string[] | null | no | Token tickers to focus on |
dataSources |
object | null | no | Global data-source toggles carried into the strategy snapshot |
assetClassSettings |
object | null | no | Per-asset-class configuration, including nested dataSources |
isPublic |
boolean | no | Make strategy publicly discoverable |
The same Pro/Max restriction applies when creating or updating a strategy with dataSources, and when syncing a linked strategy that already contains them.
GET /api/v1/users/{userId}/auto-trade-settings/strategies
List strategies with filters.
curl "{{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings/strategies?scope=owned&page=1&pageSize=25" \
-H "X-API-Key: $API_KEY"
Query parameters:
| Param | Values | Description |
|---|---|---|
scope |
all, owned, public |
Filter by ownership |
q |
string | Search by name/description |
page |
number | Page number (default: 1) |
pageSize |
number | Items per page (default: 25, max: 100) |
GET /api/v1/users/{userId}/auto-trade-settings/strategies/{strategyId}
Get strategy details.
curl {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings/strategies/{strategyId} \
-H "X-API-Key: $API_KEY"
PATCH /api/v1/users/{userId}/auto-trade-settings/strategies/{strategyId}
Update a strategy. All fields are optional.
curl -X PATCH {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings/strategies/{strategyId} \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Strategy Name",
"instructions": "New instructions for the agent"
}'
DELETE /api/v1/users/{userId}/auto-trade-settings/strategies/{strategyId}
Delete a strategy.
curl -X DELETE {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings/strategies/{strategyId} \
-H "X-API-Key: $API_KEY"
POST /api/v1/users/{userId}/auto-trade-settings/strategies/{strategyId}/sync
Re-sync auto-trade settings with the linked strategy. Takes a fresh snapshot of the strategy's current allocation, instructions, and configuration.
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings/strategies/{strategyId}/sync \
-H "X-API-Key: $API_KEY"
Strategy sync workflow:
- Link a strategy to your settings:
curl -X PATCH {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "strategyId": "<strategy-uuid>", "isActive": true }'
- Check sync status (included in GET settings response):
{
"strategyId": "uuid",
"strategySync": {
"strategyId": "uuid",
"synced": false,
"strategyUpdatedAt": "2025-06-15T14:00:00.000Z",
"settingsUpdatedAt": "2025-06-01T10:00:00.000Z"
}
}
- When
syncedisfalse, re-sync:
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/auto-trade-settings/strategies/{strategyId}/sync \
-H "X-API-Key: $API_KEY"
Arena
Deploy a public strategy to the arena leaderboard. Milo creates a custody wallet, funds it, and trades autonomously using the strategy. The strategy must be public and owned by the user. Deployment requires at least 1 SOL balance (0.01 SOL in dev). Withdrawing transfers all holdings back to the user's Milo wallet.
POST /api/v1/users/{userId}/arena/deploy
Deploy a strategy to the arena.
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/arena/deploy \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "strategyId": "<strategy-uuid>" }'
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
strategyId |
uuid | yes | ID of a public strategy owned by the user |
Response (200):
{
"data": {
"arenaUserId": "uuid",
"custodyWalletId": "uuid",
"custodyWalletAddress": "<solana-address>",
"fundingTxSignature": "<transaction-signature>",
"strategyId": "uuid"
}
}
POST /api/v1/users/{userId}/arena/withdraw
Withdraw from the arena. Transfers all holdings from the custody wallet back to the user's Milo wallet.
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/arena/withdraw \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "strategyId": "<strategy-uuid>" }'
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
strategyId |
uuid | yes | ID of the deployed strategy to withdraw |
Response (200):
{
"data": {
"arenaUserId": "uuid",
"custodyWalletId": "uuid",
"custodyWalletAddress": "<solana-address>",
"recipientWalletAddress": "<solana-address>",
"transferTxs": [
{ "tokenAddress": "<mint-address>", "amount": 1.5, "signature": "<tx-signature>" }
]
}
}
GET /api/v1/users/{userId}/arena/leaderboard
Get the arena leaderboard.
curl "{{BASE_URL}}/api/v1/users/{userId}/arena/leaderboard?timeframe=30d&page=1&pageSize=25&sortKey=pnl&sortDirection=desc" \
-H "X-API-Key: $API_KEY"
Query parameters:
| Param | Type | Values | Default |
|---|---|---|---|
timeframe |
string | 1d, 30d, 90d |
— |
page |
number | Page number | 1 |
pageSize |
number | Items per page (max: 100) | 25 |
sortKey |
string | pnl, winRate, returnPct, accountValue |
— |
sortDirection |
string | asc, desc |
— |
winRate is token-PnL based: (number of tokens with positive token PnL / total tracked tokens) * 100, excluding USDC.
Response (200):
{
"data": [
{
"strategy": "uuid",
"strategyName": "SOL Ecosystem DCA",
"ownerUserId": "uuid",
"ownerUsername": "trader1",
"pnl": 120.50,
"winRate": 0.65,
"returnPct": 12.5,
"accountValue": 1120.50,
"arenaWalletAddress": "<solana-address>",
"currentHoldings": [ ... ]
}
],
"meta": { "page": 1, "pageSize": 25, "total": 100, "pages": 4 }
}
Quests & Bones
Quests are event-driven tasks (trades, signups, etc.) that reward bones (points) upon completion. Each quest has requirements (count, sum, or streak-based) and awards bones when all requirements are met. Check quests regularly — call list_quests to see open quests, and with unclaimed=true to discover completed quests, then claim_quest to collect the bones.
GET /api/v1/users/{userId}/quests
List quests with progress and requirements. By default returns only unlocked (available) quests.
curl "{{BASE_URL}}/api/v1/users/{userId}/quests?page=1&pageSize=25" \
-H "X-API-Key: $API_KEY"
Query parameters:
| Param | Type | Values | Default |
|---|---|---|---|
unlocked |
boolean | Filter for unlocked quests (available) | true |
unclaimed |
boolean | Filter for completed but unclaimed quests | — |
claimed |
boolean | Filter for claimed quests | — |
mode |
string | completed_last |
— |
page |
number | Page number | 1 |
pageSize |
number | Items per page (max: 100) | 25 |
Response (200):
{
"data": [
{
"userId": "uuid",
"questId": "uuid",
"title": "First Trade",
"description": "Execute your first trade",
"award": 100,
"totalRequirements": 1,
"completedRequirements": 1,
"claimed": false,
"completed": true,
"unlocked": true,
"requirements": [
{
"requirementId": "uuid",
"aggregationKind": "count",
"targetValue": 1,
"currentValue": 1,
"completed": true
}
]
}
],
"meta": { "page": 1, "pageSize": 25, "total": 10, "pages": 1 }
}
POST /api/v1/users/{userId}/quests/{questId}/claim
Claim bones for a completed quest.
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/quests/{questId}/claim \
-H "X-API-Key: $API_KEY"
Response (200):
{ "data": null }
Returns 404 if the quest is not found or already claimed.
GET /api/v1/users/{userId}/quests/bones
Get the user's bones balance.
curl "{{BASE_URL}}/api/v1/users/{userId}/quests/bones" \
-H "X-API-Key: $API_KEY"
Response (200):
{
"data": {
"userId": "uuid",
"username": "alice",
"balance": 500,
"unclaimed": 100
}
}
balance— total bones already claimedunclaimed— bones from completed quests not yet claimed (callclaim_questto collect)
Wallet Actions
POST /api/v1/wallets/{walletId}/actions/send
Send tokens from your Milo wallet to a recipient address.
curl -X POST {{BASE_URL}}/api/v1/wallets/{walletId}/actions/send \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipient": "<solana-address>",
"token": "<token-mint-address>",
"amount": 1.5
}'
Response (202):
{ "data": "<transaction-signature>" }
Note: For native SOL, use the SOL mint address:
So11111111111111111111111111111111111111112. If a JSON body includeswalletId, it must match the{walletId}path parameter.
Orders
POST /api/v1/wallets/{walletId}/orders
Create a new order, optionally with take-profit and stop-loss dependants.
curl -X POST {{BASE_URL}}/api/v1/wallets/{walletId}/orders \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tokenAddress": "So11111111111111111111111111111111111111112",
"type": "buy",
"status": "active",
"expiresAt": "2026-12-31T23:59:59.000Z",
"payload": {
"type": "buy",
"amount": { "type": "absolute_usd", "amount": 50 },
"trigger": { "type": "absolute", "trigger": "price", "operator": "gte", "value": 0 },
"execution": {}
},
"takeProfits": [
{ "percentage": 50, "profitPercentage": 20 },
{ "percentage": 50, "profitPercentage": 50 }
],
"stopLosses": [
{ "percentage": 100, "lossPercentage": 15 }
]
}'
Response includes the main order plus a dependants array:
{
"data": {
"id": "...",
"type": "buy",
"status": "active",
"dependants": [
{ "type": "take_profit", "order": { "id": "...", "parentId": "...", "subType": "take_profit", "status": "draft" } },
{ "type": "take_profit", "order": { "id": "...", "parentId": "...", "subType": "take_profit", "status": "draft" } },
{ "type": "stop_loss", "order": { "id": "...", "parentId": "...", "subType": "stop_loss", "status": "draft" } }
]
}
}
Take-profit / Stop-loss ladder (optional):
takeProfits array — each item:
percentage(1–100) — percent of position to sellprofitPercentage(> 0) — profit percent to trigger the TP
stopLosses array — each item:
percentage(1–100) — percent of position to selllossPercentage(1–100) — loss percent to trigger the SL
Dependant creation flow (enforced):
- Main order is created first.
- TP and SL dependants are created sequentially as draft sell children (
parentId= main order ID). - Dependant failures are captured per dependant entry while the main order still returns
201.
Guardrails (always enforce mode):
takeProfits.length <= 5stopLosses.length <= 5takeProfits.length + stopLosses.length <= 8
Violations return 400 bad_request with a clear validation message. Dependants use relative triggers (rise for TP, drop for SL).
Order payload structure:
Buy order amounts:
{ "type": "absolute", "amount": 1000000 }- Raw token amount{ "type": "absolute_usd", "amount": 50 }- USD equivalent
Sell order amounts (additional option):
{ "type": "relative", "percentage": 50 }- Percentage of position
Trigger types:
- Market order:
{ "type": "absolute", "trigger": "price", "operator": "gte", "value": 0 } - Limit buy:
{ "type": "absolute", "trigger": "price", "operator": "lte", "value": 150.00 } - Stop loss:
{ "type": "relative", "trigger": "price", "operator": "drop", "value": 10 }(10% drop) - Take profit:
{ "type": "relative", "trigger": "price", "operator": "rise", "value": 25 }(25% rise)
Market orders require expiresAt, and it must be within 120 minutes of the request time.
Execution options (all optional):
slippagePercentage- Max slippage (0-100)priorityFee- Priority fee in SOL lamportsplatformFeeBps- Platform fee override in basis points
Linking to a thesis: Pass positionThesisId (UUID) in the body to attach the order (and its TP/SL dependants) to a position thesis.
GET /api/v1/users/{userId}/orders
List orders with filters.
curl "{{BASE_URL}}/api/v1/users/{userId}/orders?status=active&type=buy&page=1&pageSize=25" \
-H "X-API-Key: $API_KEY"
Query parameters:
| Param | Type | Values |
|---|---|---|
status |
string | active, paused, error, fulfilled, archived, draft |
type |
string | buy, sell |
tokenAddress |
string | Filter by token |
page |
number | Page number (default: 1, max: 100) |
pageSize |
number | Items per page (default: 25, max: 100) |
GET /api/v1/users/{userId}/orders/{orderId}
Get order details.
curl {{BASE_URL}}/api/v1/users/{userId}/orders/{orderId} \
-H "X-API-Key: $API_KEY"
POST /api/v1/users/{userId}/orders/{orderId}/pause
Pause an active order.
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/orders/{orderId}/pause \
-H "X-API-Key: $API_KEY"
POST /api/v1/users/{userId}/orders/{orderId}/activate
Activate a draft or paused order.
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/orders/{orderId}/activate \
-H "X-API-Key: $API_KEY"
DELETE /api/v1/users/{userId}/orders/{orderId}
Archive (delete) an order.
curl -X DELETE {{BASE_URL}}/api/v1/users/{userId}/orders/{orderId} \
-H "X-API-Key: $API_KEY"
Conversations
Milo uses an async conversation model. You send a message and the agent processes it in the background. Poll the messages endpoint for the response using the processing flag.
POST /api/v1/users/{userId}/conversations
Create a new conversation and send the first message.
curl -X POST {{BASE_URL}}/api/v1/users/{userId}/conversations \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "What is the current ma
…(truncated)