Skip to content

API reference

Base: https://dayprotocol.com/api/v1
OpenAPI: openapi.json
Routes: GET /api/v1/day/routes · Errors: GET /api/v1/day/errors

Money: /api/v1/wallets/{address}/… · Public: /api/v1/day/…


X-API-Key: <key>
RoleEnvEndpoints
ownerDAY_OWNER_API_KEYSroute, withdraw, auto-pay, bridge rescue, PUT auto-yield, webhooks, cage policy
agentDAY_AGENT_API_KEYSGET position/portfolio/performance/funding/preview, GET auto-yield
keeperDAY_KEEPER_API_KEYSsurvival, harvest/batch, map/stats refresh
public / permissionlessstatus, venues, strategies, deposit plans, harvest poke, balances
HTTPcodeWhen
401INVALID_API_KEY / UNAUTHORIZEDKey missing or not in allowlist
403UNAUTHORIZEDWrong role
403WALLET_SCOPEWallet not allowed for this key

X-Role is ignored. Do not send it for privilege.

import { DayClient } from "@dayprotocol/sdk";
const day = new DayClient({
baseUrl: "https://dayprotocol.com", // origin only — SDK never double-prefixes /api/v1
apiKey: process.env.DAY_OWNER_API_KEY,
});

TopicRule
opportunityIdA permanent dayop handle (dayop487e57366b) — read it from a response, never construct it. strategyId is a deprecated input alias; responses emit opportunityId.
protocolWho runs the opportunity (aave, kamino, suilend). “venue” and “strategy” are retired terms.
amountsInteger micros strings ("1000000" = 1 USDC).
feesSee Fees for every rate. Live schedule: GET /api/v1/day/fees — never hard-code rates client-side.
plansPrepare-only: ownerMustSign: true, submitted: false unless broadcast + real digest.
APYLive or null — never fabricated.
status codes200 done · 202 prepared · 422 blocked · 401/403 auth · 429 rate limit
HomesLabel
Sui, SolanaExecutable (phase-1 route homes)
Base, ArbitrumMap (discovery / prepare; broadcast off until GO + writeReady)

Terminal window
curl -sS https://dayprotocol.com/api/v1/day/status

Codegen source. Alias: /openapi.json.

Sui / Solana / Base / Arbitrum package ids + Map vs Executable labels.

Versioned error + blocker enum. Renaming a code is breaking.

Per-venue APY table; status: "unavailable" when live read fails.


One call per page. Don’t fan out — these return exactly what a page renders.

GET /api/v1/day/home · public · getHome()

Section titled “GET /api/v1/day/home · public · getHome()”

Everything the homepage renders, in one call: opportunities[], protocols[], markets[], already filtered and sorted. Rows are trimmed to what a table shows; internal fields and duplicate APY aliases aren’t emitted.

Terminal window
curl -sS 'https://dayprotocol.com/api/v1/day/home' | jq '{opportunities:(.opportunities|length), protocols:(.protocols|length)}'

GET /api/v1/day/portfolio?wallet=… · public · getPortfolio({ wallet })

Section titled “GET /api/v1/day/portfolio?wallet=… · public · getPortfolio({ wallet })”

The opportunities a wallet deposited into, how much went in, what it’s worth now, the ROI, and the deposit transaction as proof. Returns { schemaVersion: "day-wallet-portfolio.v1", wallet, count, positions[], totalDepositedUsd, totalCurrentUsd }.

Terminal window
curl -sS 'https://dayprotocol.com/api/v1/day/portfolio?wallet=0x…' | jq '.positions[] | {opportunityId,protocol,depositedMicros,currentMicros,roiPct,txId}'
  • txId is always present — a position without an on-chain transaction is a claim with no proof.
  • roiPct is null when we can’t compute it (no price, no cost basis) — never 0. 0 means you broke even; null means unknown.
  • Cost basis is net of routing legs, so ROI never overstates by the cost of the swap and bridge.
  • A wallet can only ever read its own positions.

GET /api/v1/day/activity?wallet=… · public · getActivity({ wallet })

Section titled “GET /api/v1/day/activity?wallet=… · public · getActivity({ wallet })”

The full action log, newest first, grouped by planId. Returns { schemaVersion: "day-wallet-activity.v1", wallet, count, activity[] }.

One row per leg, not per action. A cross-chain deposit is bridge → swap → deposit, and all three are rows — linked by planId, ordered by legIndex. A log showing only the final deposit would make it impossible to audit where the money went.

Terminal window
curl -sS 'https://dayprotocol.com/api/v1/day/activity?wallet=0x…' | jq '.activity[] | {planId,legIndex,kind,txId,amountOut,quotedAmountOut,status}'
fieldmeaning
planId, legIndexgroups and orders the legs of one action
kindswap | bridge | deposit | withdraw
txId, chain, destChainon-chain proof; destChain set for bridges
amountIn, amountOutamountOut is the actual settled amount from the receipt — never the quote
quotedAmountOutwhat was quoted, kept alongside so slippage is auditable
status, errorfailed legs are rows too — a reverted bridge is what a user most needs to see

GET /api/v1/day/venues · public · listVenues()

Section titled “GET /api/v1/day/venues · public · listVenues()”
Terminal window
curl -sS 'https://dayprotocol.com/api/v1/day/venues' | jq '.executionMode,.launchHomes,(.venues|length)'

GET /api/v1/day/strategies · public · listStrategies()

Section titled “GET /api/v1/day/strategies · public · listStrategies()”
Terminal window
curl -sS https://dayprotocol.com/api/v1/day/strategies | jq '.strategies[] | {strategyId,chain,ready}'

GET /api/v1/day/strategies/{id} · public · getStrategy(id)

Section titled “GET /api/v1/day/strategies/{id} · public · getStrategy(id)”

Bare id or form-* alias. Unknown → 404.

POST /api/v1/day/strategies/deposit/plan · public · prepareStrategyDeposit

Section titled “POST /api/v1/day/strategies/deposit/plan · public · prepareStrategyDeposit”
Terminal window
curl -sS -X POST https://dayprotocol.com/api/v1/day/strategies/deposit/plan \
-H 'content-type: application/json' \
-d '{"strategyId":"suilend","amountMicros":"1000000"}'

Principal fee 0. Same shape for every venue (venue-agnostic).

POST /api/v1/day/strategies/withdraw/plan · public · prepareStrategyWithdraw

Section titled “POST /api/v1/day/strategies/withdraw/plan · public · prepareStrategyWithdraw”

Canonical paths (no /day/ segment under wallets):

MethodPathAuthSDK
GET/api/v1/wallets/{address}/positionowner|agentgetPosition
GET/api/v1/wallets/{address}/portfolioowner|agentgetPortfolio
GET/api/v1/wallets/{address}/performanceowner|agentgetPerformance
GET/api/v1/wallets/{address}/fundingowner|agentgetFunding
POST/api/v1/wallets/{address}/previewowner|agentpreviewRoute
POST/api/v1/wallets/{address}/routeownerrouteYield
POST/api/v1/wallets/{address}/harvestpermissionlessharvest
POST/api/v1/wallets/{address}/withdrawownerwithdraw
POST/api/v1/wallets/{address}/auto-payownerenableAutoPay
POST/api/v1/wallets/{address}/rebalanceowner
GET/PUT/api/v1/wallets/{address}/auto-yieldowner|agent / ownergetAutoYield / setAutoYield
POST/api/v1/wallets/batch/positionsowner|agentbatchPositions
Terminal window
export BASE=https://dayprotocol.com/api/v1
export KEY=$DAY_OWNER_API_KEY
export W=0xYOUR_WALLET
curl -sS -H "X-API-Key: $KEY" "$BASE/wallets/$W/portfolio"
curl -sS -H "X-API-Key: $KEY" "$BASE/wallets/$W/performance"
curl -sS -X POST -H "X-API-Key: $KEY" -H 'content-type: application/json' \
-d "{\"amountMicros\":\"1000000\"}" "$BASE/wallets/$W/preview"
SituationHTTPBody
No stake / no yield422mode: "no_op_harvest", outcome: "blocked"
Successful ledger harvest200gross / fee (see Fees) / net
Prepare-only edge202prepared: true

Never invent gross yield from client body in production.


PathAssets
GET /api/v1/day/sui/wallet-balance?address=Sui assets
GET /api/v1/day/solana/wallet-balance?address=SOL + USDC
GET /api/v1/day/base/wallet-balance?address=ETH + USDC
GET /api/v1/day/arbitrum/wallet-balance?address=ETH + USDC

RPC failure → leg N/A (never invent).

Enablement: GET /api/v1/day/base/enablement, GET /api/v1/day/arbitrum/enablement.


PathChain
POST /api/v1/day/jupiter/planSolana
POST /api/v1/day/turbos/planSui
POST /api/v1/day/evm/swap/planBase / Arbitrum

Fee line items + slippage bounds (default 100 bps, max 500). ownerMustSign: true. No custody.


PathAuth
POST /api/v1/day/bridge/planpublic
POST /api/v1/day/bridge/deliveryowner|keeper
POST /api/v1/day/bridge/rescueowner

Lifecycle: prepare → owner sign → delivery tracking → credit only after proof.
Rescue destination locked to owner. Fee/risk disclosure on every plan.
Autopilot is within-chain only — never silent cross-chain rebalance.


PathAuth
GET /api/v1/day/webhooks/events/typespublic
POST /api/v1/day/webhooksowner (returns hmacKey once)
GET /api/v1/day/webhooksowner
POST /api/v1/day/webhooks/{id}/rotateowner
DELETE /api/v1/day/webhooks/{id}owner
GET /api/v1/day/webhooks/eventsowner|agent

Events: deposit.settled, withdraw.settled, harvest.completed, bridge.delivered, bridge.failed, position.updated, apy.updated.

HMAC: sign `$\{timestamp\}.$\{rawBody\}` with hmacKey; header X-DAY-Signature: t=<unix>,v1=<hex>.


Six actions: ROUTE · EXIT · REBALANCE · HARVEST · COMPOUND · AUTOPAY.
Owner signs; Day never holds keys; destination-lock on venues / payees / owner.

Base: /api/v1/day/base/cage, /api/v1/wallets/\{address\}/base/*
Arbitrum: /api/v1/day/arbitrum/cage, POST …/cage/prepare


Terminal window
export BASE=https://dayprotocol.com/api/v1
export KEY=$DAY_OWNER_API_KEY
export W=demo-wallet-1
# 1) discover
curl -sS "$BASE/day/strategies" | jq '.strategies[:3]'
# 2) deposit plan (prepare)
curl -sS -X POST "$BASE/day/strategies/deposit/plan" \
-H 'content-type: application/json' \
-d '{"strategyId":"suilend","amountMicros":"1000000"}' | jq '{status,feeMicros,ownerMustSign}'
# 3) position / portfolio
curl -sS -H "X-API-Key: $KEY" "$BASE/wallets/$W/position"
curl -sS -H "X-API-Key: $KEY" "$BASE/wallets/$W/portfolio"
# 4) harvest (permissionless poke)
curl -sS -X POST -H 'content-type: application/json' \
-d '{}' "$BASE/wallets/$W/harvest" | jq '{httpStatus,mode,grossYieldMicros,protocolFeeMicros}'
const day = new DayClient({ baseUrl: "https://dayprotocol.com", apiKey: process.env.DAY_OWNER_API_KEY });
await day.listStrategies();
await day.prepareStrategyDeposit({ strategyId: "suilend", amountMicros: "1000000" });
await day.getPortfolio(process.env.DAY_WALLET_ADDRESS!);
await day.getPerformance(process.env.DAY_WALLET_ADDRESS!);
await day.harvest(process.env.DAY_WALLET_ADDRESS!, { execute: false });

Terminal window
npm install github:dayprotocol/sdk
# package name: @dayprotocol/sdk
MethodRoute
getPosition(wallet)GET …/position
getPortfolio(wallet)GET …/portfolio
getPerformance(wallet)GET …/performance
batchPositions(wallets)POST …/batch/positions
previewRoute / routeYieldPOST …/preview · …/route
harvest / withdrawPOST …/harvest · …/withdraw
venueApyTable / errorCatalogGET …/venues/apy · …/errors
bridgePlan / bridgeRescuePlanbridge/*
baseWalletBalance / arbitrumWalletBalancechain balances

When paths, auth, or examples change, update in the same change:

  1. runtime/config/openapi.mjs
  2. npm run check:docs-openapi

Live OpenAPI always wins for machines: openapi.json.