v0.1.0-draft OpenAPI 3.1.0 REST · JSON

MoonUp Account API

Programmatic read access to a MoonUp account, plus the ability to withdraw USDC to an external wallet.

Everything the app shows you about your account is available here: balances, token holdings, structured trades and their performance, and the activity feed. Writes are limited to withdrawals.

Base URL

https://api.moonup.io/v1

Authentication

Every request carries an API key as a bearer token:

Authorization: Bearer mu_live_7Kd2...

Keys are created in Settings → API keys, shown once, and can be revoked at any time. Each key carries a set of scopes; a key issued without write:withdraw cannot move funds, which is the recommended default for anything that only needs to read.

Basket endpoints need no key — they return the same catalogue for everyone.

Identifying assets

Tokens and basket constituents are identified by their CAIP-19 id, which encodes the chain and the token address. symbol and ticker are display labels: they are neither unique nor stable, so never use them as a key.

Amounts

USD and USDC amounts are JSON numbers in display units — 118.42 means 118.42 USDC, never base units. Token quantities are lots, also in display units, and may be fractional.

Accounts, sub-accounts, and paper mode

An account can hold several sub-accounts, and every account-scoped path is addressed by subAccountId. A sub-account is either live, settling on Solana, or paper (isPaper: true), which simulates execution against a play balance. Both expose the identical shape; only isPaper and the source of the cash differ. Withdrawals are rejected on paper sub-accounts.

Errors

Errors return the matching HTTP status and a JSON body with a stable machine-readable code. Read code, not message — messages may be reworded.

Rate limits

Rate limits apply per key. A throttled request returns 429 with code rate_limited and a Retry-After header.

ServerEnvironment
https://api.moonup.io/v1Production
http://localhost:8911/v1Local mock server

Authentication

httpbearer mu_<env>_<random>

An API key created in Settings → API keys, sent as a bearer token.

Scopes:

ScopeGrants
read:account/me, balances, token holdings
read:tradesStructured trades, NAV, lots
read:activityThe activity feed
write:withdrawPOST /withdrawals

A key without write:withdraw is rejected on the withdrawal path with 403 insufficient_scope.

Identity

Who you are and which accounts you hold.

GET /v1/me

Your account and every sub-account in it

Start here — every other path needs a subAccountId from this response.

StatusDescriptionBody
200 OK object
401 The API key is missing, malformed, revoked, or expired. Error
429 Too many requests for this key. Error

Account

Balances and holdings for one sub-account.

GET /v1/accounts/{subAccountId}/balance

What a sub-account is worth, and how the money is being used

ParameterInTypeDescription
subAccountIdrequired path string (uuid) From GET /me.
StatusDescriptionBody
200 OK Balance
401 The API key is missing, malformed, revoked, or expired. Error
404 No such resource on this account. Error
429 Too many requests for this key. Error
GET /v1/accounts/{subAccountId}/tokens

Token holdings

Everything the sub-account holds, both loose in the wallet and working inside trades.

ParameterInTypeDescription
subAccountIdrequired path string (uuid) From GET /me.
heldVia query WALLET | TRADE Return only wallet-held or only trade-held tokens.
StatusDescriptionBody
200 OK object
401 The API key is missing, malformed, revoked, or expired. Error
404 No such resource on this account. Error
429 Too many requests for this key. Error

Trades

Structured trades and their performance.

GET /v1/accounts/{subAccountId}/trades

Structured trades on a sub-account

ParameterInTypeDescription
subAccountIdrequired path string (uuid) From GET /me.
status query ACTIVE | PAUSED | CLOSED | UNWOUND
includeCash query boolean Also return the cash ledger, which records deposits and withdrawals. default false
StatusDescriptionBody
200 OK object
401 The API key is missing, malformed, revoked, or expired. Error
404 No such resource on this account. Error
429 Too many requests for this key. Error
GET /v1/trades/{tradeId}

One trade, with its full event history

ParameterInTypeDescription
tradeIdrequired path string (uuid)
StatusDescriptionBody
200 OK Trade
401 The API key is missing, malformed, revoked, or expired. Error
404 No such resource on this account. Error
429 Too many requests for this key. Error
GET /v1/trades/{tradeId}/nav

A trade's value over time

Value, protection floor and accumulated gain at each point in the period.

ParameterInTypeDescription
tradeIdrequired path string (uuid)
period query 1D | 1W | 1M | ALL default 1M
StatusDescriptionBody
200 OK object
401 The API key is missing, malformed, revoked, or expired. Error
404 No such resource on this account. Error
429 Too many requests for this key. Error

Activity

The account's activity feed.

GET /v1/accounts/{subAccountId}/activity

The activity feed

Newest first, cursor-paginated. The same feed the app shows.

ParameterInTypeDescription
subAccountIdrequired path string (uuid) From GET /me.
cursor query string Opaque cursor from a previous response's nextCursor.
limit query integer default 50
since query string (date-time) Only return activity at or after this time.
StatusDescriptionBody
200 OK object
401 The API key is missing, malformed, revoked, or expired. Error
404 No such resource on this account. Error
429 Too many requests for this key. Error

Baskets

The catalogue of baskets available to trade. No key required.

GET /v1/baskets no key

Baskets available to trade

Catalogue data, the same for everyone. No API key required.

ParameterInTypeDescription
type query Equity | Yield
StatusDescriptionBody
200 OK object
GET /v1/baskets/{basketId} no key

One basket, with its current constituent weights

ParameterInTypeDescription
basketIdrequired path string
StatusDescriptionBody
200 OK Basket
404 No such resource on this account. Error

Transfers

Moving USDC out to an external wallet.

POST /v1/withdrawals

Withdraw USDC to an external wallet

Sends available USDC from a live sub-account to an external Solana address. Requires the write:withdraw scope.

You name a destination, never a source — funds always come from the wallet belonging to the key's own account.

The amount is checked against availableUsd: cash committed to a trade change that has not finished settling cannot be withdrawn.

Idempotency-Key is required. Repeating a request with the same key within 24 hours returns the original result instead of sending twice — retry safely on a timeout.

Returns as soon as the transfer is submitted to the network. Poll GET /withdrawals/{withdrawalId} for confirmation.

ParameterInTypeDescription
Idempotency-Keyrequired header string A unique string you choose per withdrawal. Reuse it when retrying.
A fixed amount
{
  "subAccountId": "6b0c1d2e-3f40-4a5b-8c6d-7e8f90a1b2c3",
  "destinationAddress": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
  "amountUsd": 250
}
The entire available balance
{
  "subAccountId": "6b0c1d2e-3f40-4a5b-8c6d-7e8f90a1b2c3",
  "destinationAddress": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
  "max": true
}
StatusDescriptionBody
202 Submitted to the network. Withdrawal
400 The request cannot be carried out. Error
401 The API key is missing, malformed, revoked, or expired. Error
403 The key is valid but lacks the scope this operation needs. Error
429 Too many requests for this key. Error
GET /v1/withdrawals/{withdrawalId}

The status of a withdrawal

ParameterInTypeDescription
withdrawalIdrequired path string (uuid)
StatusDescriptionBody
200 OK Withdrawal
401 The API key is missing, malformed, revoked, or expired. Error
404 No such resource on this account. Error
429 Too many requests for this key. Error

Schemas

Error

FieldTypeDescription
errorrequired object

User

FieldTypeDescription
id string
email string (email)
handle string | null
signupDate string (date-time)

Wallet

The wallet that settles a live account's trades and holds its USDC.

FieldTypeDescription
id string (uuid)
publicAddress string
chain string CAIP-2 chain id.
createdDate string (date-time)
lastTransactionDate string (date-time)

SubAccount

FieldTypeDescription
id string (uuid)
label string
isPaper boolean true = simulated against a play balance; false = settles on-chain.
created string (date-time)

Account

FieldTypeDescription
id string (uuid)
label string | null
wallet Wallet | null Null on paper accounts, which hold no wallet.
subAccounts SubAccount[]

Balance

What one sub-account is worth right now, broken down by how the money is being used. totalUsd = availableUsd + allocatedUsd, and allocatedUsd = protectedUsd + atRiskUsd.

FieldTypeDescription
subAccountId string (uuid)
isPaper boolean
currency "USDC"
totalUsd number
availableUsd number Settled cash not committed to any trade. This is the most you can withdraw.
committedUsd number Cash reserved by a trade change that has not finished settling.
allocatedUsd number Cash working inside active trades.
protectedUsd number The part of allocated capital covered by the trades' protection floors.
atRiskUsd number The part of allocated capital exposed to market moves.
pnl map<string, object> Gain or loss by period, keyed 1D, 1W, 1M, ALL.
asOf string (date-time)

Token

One holding. Identified by caip19 alone.

FieldTypeDescription
caip19 string
symbol string Display only. Not unique
ticker string | null Display only. The underlying equity ticker, where one exists.
name string | null
imageUrl string | null
lots number Quantity in display units. May be fractional.
priceUsd number | null
valueUsd number | null
assetClass CASH | EQUITY | YIELD
heldVia WALLET | TRADE WALLET = sitting loose in the wallet. TRADE = working inside a structured trade.
tradeId string (uuid) | null Set when heldVia is TRADE.

TradeEvent

One change to a trade, in the order it happened.

FieldTypeDescription
id string (uuid)
eventType CREATED | REBALANCED | CUSHION_ADJUSTED | UPSIZED | DOWNSIZED | PAUSED | CLOSED | DEPOSIT | WITHDRAWAL
triggerSource USER | AUTOMATED Whether you asked for this change or the strategy made it on its own.
status PENDING | EXECUTED | ABORTED | PARTIAL EXECUTED = fully settled. PENDING = still settling. ABORTED = nothing moved, the trade is unchanged. PARTIAL = the change went through incompletely and is being resolved.
capitalChangedUsd number | null Positive when money went in, negative when it came out. Null when the event moved no money.
scheduledFor string (date-time) | null When a deferred change is expected to run, e.g. one waiting on market hours.
executedAt string (date-time)

Trade

FieldTypeDescription
id string (uuid)
subAccountId string (uuid)
tradeType CPPI | USDC_CASH CPPI = a protected basket position. USDC_CASH = the cash ledger, which records deposits and withdrawals rather than a market position.
status ACTIVE | PAUSED | CLOSED | UNWOUND
basketId string | null Null on the cash ledger.
basketName string | null
floorPct number | null The protection floor, as a fraction of the trade's value.
multiplier number How aggressively the strategy uses the cushion above the floor.
notionalUsd number
protectedUsd number
nav NavPoint
createdAt string (date-time)
closedAt string (date-time) | null
events TradeEvent[] Returned by GET /trades/{tradeId} only.

NavPoint

FieldTypeDescription
timestampMs number
nav number The trade's total value at this moment.
equityNav number The part held in the equity basket.
yieldNav number The part held in the yield sleeve.
accPnlUsd number Gain or loss since the trade opened.
floorUsd number The protected value at this moment.
notionalUsd number Capital put in

ActivityItem

One row of the activity feed — the same feed the app shows. It lists the moments that matter: opening a trade, adding to it, reducing it, changing its protection, and withdrawing cash. Routine automated rebalances are left out; read GET /trades/{tradeId} for the full event history of a trade.

FieldTypeDescription
id string (uuid)
tradeId string (uuid)
kind trade | deposit | withdrawal
eventType CREATED | UPSIZED | DOWNSIZED | CUSHION_ADJUSTED | WITHDRAWAL
title string
subtitle string
amountUsd number | null
amountVariant cash | cashChg cash = an absolute amount, cashChg = a signed change.
basketImageUrl string | null
executedAt string (date-time)

BasketWeight

FieldTypeDescription
caip19 string
symbol string
ticker string
weightPct number
lots number
priceUsd number

Basket

FieldTypeDescription
basketId string
displayName string
description string | null
ticker string | null
image string | null
type Equity | Yield Equity baskets carry market exposure; yield baskets are the safe sleeve.
category string | null
volatility string | null
tokenCount integer | null
lastUpdated string | null
strategyOverview string | null
sectorAllocation object[]
weights BasketWeight[] Returned by GET /baskets/{basketId} only.

WithdrawalRequest

FieldTypeDescription
subAccountIdrequired string (uuid)
destinationAddressrequired string The external Solana address to send to. Check it carefully — transfers cannot be reversed.
amountUsd number In USDC. Required unless max is true.
max boolean Send the entire available balance, sized so no dust is left behind. Also the only way to withdraw an amount below the minimum.

Withdrawal

FieldTypeDescription
id string (uuid)
signature string The Solana transaction signature.
status SUBMITTED | CONFIRMED | FAILED
amountUsd number
destinationAddress string
submittedAt string (date-time)