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>| Role | Env | Endpoints |
|---|---|---|
| owner | DAY_OWNER_API_KEYS | route, withdraw, auto-pay, bridge rescue, PUT auto-yield, webhooks, cage policy |
| agent | DAY_AGENT_API_KEYS | GET position/portfolio/performance/funding/preview, GET auto-yield |
| keeper | DAY_KEEPER_API_KEYS | survival, harvest/batch, map/stats refresh |
| public / permissionless | — | status, venues, strategies, deposit plans, harvest poke, balances |
| HTTP | code | When |
|---|---|---|
| 401 | INVALID_API_KEY / UNAUTHORIZED | Key missing or not in allowlist |
| 403 | UNAUTHORIZED | Wrong role |
| 403 | WALLET_SCOPE | Wallet 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,});Conventions
Section titled “Conventions”| Topic | Rule |
|---|---|
| opportunityId | A permanent dayop handle (dayop487e57366b) — read it from a response, never construct it. strategyId is a deprecated input alias; responses emit opportunityId. |
| protocol | Who runs the opportunity (aave, kamino, suilend). “venue” and “strategy” are retired terms. |
| amounts | Integer micros strings ("1000000" = 1 USDC). |
| fees | See Fees for every rate. Live schedule: GET /api/v1/day/fees — never hard-code rates client-side. |
| plans | Prepare-only: ownerMustSign: true, submitted: false unless broadcast + real digest. |
| APY | Live or null — never fabricated. |
| status codes | 200 done · 202 prepared · 422 blocked · 401/403 auth · 429 rate limit |
Multi-chain honesty
Section titled “Multi-chain honesty”| Homes | Label |
|---|---|
| Sui, Solana | Executable (phase-1 route homes) |
| Base, Arbitrum | Map (discovery / prepare; broadcast off until GO + writeReady) |
GET /api/v1/day/status · public
Section titled “GET /api/v1/day/status · public”curl -sS https://dayprotocol.com/api/v1/day/statusGET /api/v1/openapi.json · public
Section titled “GET /api/v1/openapi.json · public”Codegen source. Alias: /openapi.json.
GET /api/v1/day/packages · public
Section titled “GET /api/v1/day/packages · public”Sui / Solana / Base / Arbitrum package ids + Map vs Executable labels.
GET /api/v1/day/errors · public
Section titled “GET /api/v1/day/errors · public”Versioned error + blocker enum. Renaming a code is breaking.
GET /api/v1/day/venues/apy · public
Section titled “GET /api/v1/day/venues/apy · public”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.
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 }.
curl -sS 'https://dayprotocol.com/api/v1/day/portfolio?wallet=0x…' | jq '.positions[] | {opportunityId,protocol,depositedMicros,currentMicros,roiPct,txId}'txIdis always present — a position without an on-chain transaction is a claim with no proof.roiPctisnullwhen we can’t compute it (no price, no cost basis) — never0.0means you broke even;nullmeans 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.
curl -sS 'https://dayprotocol.com/api/v1/day/activity?wallet=0x…' | jq '.activity[] | {planId,legIndex,kind,txId,amountOut,quotedAmountOut,status}'| field | meaning |
|---|---|
planId, legIndex | groups and orders the legs of one action |
kind | swap | bridge | deposit | withdraw |
txId, chain, destChain | on-chain proof; destChain set for bridges |
amountIn, amountOut | amountOut is the actual settled amount from the receipt — never the quote |
quotedAmountOut | what was quoted, kept alongside so slippage is auditable |
status, error | failed legs are rows too — a reverted bridge is what a user most needs to see |
Opportunities & discovery
Section titled “Opportunities & discovery”GET /api/v1/day/venues · public · listVenues()
Section titled “GET /api/v1/day/venues · public · listVenues()”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()”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”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”Wallet money surface
Section titled “Wallet money surface”Canonical paths (no /day/ segment under wallets):
| Method | Path | Auth | SDK |
|---|---|---|---|
| GET | /api/v1/wallets/{address}/position | owner|agent | getPosition |
| GET | /api/v1/wallets/{address}/portfolio | owner|agent | getPortfolio |
| GET | /api/v1/wallets/{address}/performance | owner|agent | getPerformance |
| GET | /api/v1/wallets/{address}/funding | owner|agent | getFunding |
| POST | /api/v1/wallets/{address}/preview | owner|agent | previewRoute |
| POST | /api/v1/wallets/{address}/route | owner | routeYield |
| POST | /api/v1/wallets/{address}/harvest | permissionless | harvest |
| POST | /api/v1/wallets/{address}/withdraw | owner | withdraw |
| POST | /api/v1/wallets/{address}/auto-pay | owner | enableAutoPay |
| POST | /api/v1/wallets/{address}/rebalance | owner | — |
| GET/PUT | /api/v1/wallets/{address}/auto-yield | owner|agent / owner | getAutoYield / setAutoYield |
| POST | /api/v1/wallets/batch/positions | owner|agent | batchPositions |
export BASE=https://dayprotocol.com/api/v1export KEY=$DAY_OWNER_API_KEYexport 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"Harvest notes
Section titled “Harvest notes”| Situation | HTTP | Body |
|---|---|---|
| No stake / no yield | 422 | mode: "no_op_harvest", outcome: "blocked" |
| Successful ledger harvest | 200 | gross / fee (see Fees) / net |
| Prepare-only edge | 202 | prepared: true |
Never invent gross yield from client body in production.
Chain balances (public)
Section titled “Chain balances (public)”| Path | Assets |
|---|---|
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.
Swaps (prepare-only)
Section titled “Swaps (prepare-only)”| Path | Chain |
|---|---|
POST /api/v1/day/jupiter/plan | Solana |
POST /api/v1/day/turbos/plan | Sui |
POST /api/v1/day/evm/swap/plan | Base / Arbitrum |
Fee line items + slippage bounds (default 100 bps, max 500). ownerMustSign: true. No custody.
Bridge (Mayan)
Section titled “Bridge (Mayan)”| Path | Auth |
|---|---|
POST /api/v1/day/bridge/plan | public |
POST /api/v1/day/bridge/delivery | owner|keeper |
POST /api/v1/day/bridge/rescue | owner |
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.
Webhooks
Section titled “Webhooks”| Path | Auth |
|---|---|
GET /api/v1/day/webhooks/events/types | public |
POST /api/v1/day/webhooks | owner (returns hmacKey once) |
GET /api/v1/day/webhooks | owner |
POST /api/v1/day/webhooks/{id}/rotate | owner |
DELETE /api/v1/day/webhooks/{id} | owner |
GET /api/v1/day/webhooks/events | owner|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>.
Cage (Base / Arbitrum)
Section titled “Cage (Base / Arbitrum)”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
Walkthrough
Section titled “Walkthrough”export BASE=https://dayprotocol.com/api/v1export KEY=$DAY_OWNER_API_KEYexport W=demo-wallet-1
# 1) discovercurl -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 / portfoliocurl -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 });npm install github:dayprotocol/sdk# package name: @dayprotocol/sdk| Method | Route |
|---|---|
getPosition(wallet) | GET …/position |
getPortfolio(wallet) | GET …/portfolio |
getPerformance(wallet) | GET …/performance |
batchPositions(wallets) | POST …/batch/positions |
previewRoute / routeYield | POST …/preview · …/route |
harvest / withdraw | POST …/harvest · …/withdraw |
venueApyTable / errorCatalog | GET …/venues/apy · …/errors |
bridgePlan / bridgeRescuePlan | bridge/* |
baseWalletBalance / arbitrumWalletBalance | chain balances |
OpenAPI lockstep
Section titled “OpenAPI lockstep”When paths, auth, or examples change, update in the same change:
runtime/config/openapi.mjsnpm run check:docs-openapi
Live OpenAPI always wins for machines: openapi.json.