# Melaya > Agentic platform for trading, research and automation. Built by an operator, > for operators. 275+ tools, 100+ specialized subagents, secure connectors, > RAG with isolated vector stores, full audit trail, and a Rust-native trading > engine with 99-venue market-data coverage. One studio for building, running, > and operating AI agents across any workflow. Melaya is a standalone agentic platform headquartered in the United Arab Emirates. The product is solo-built by Antoine Roche, who spent the previous decade shipping production trading systems at BNP Paribas CIB (EQD eCommerce support manager, 2018-2022), SingularityDAO (product manager, DeFi yield vaults and on-chain analytics, 2021-2025), and Singularity Venture Hub (head of trading operations, HFT market making and execution tooling, 2025-2026). Melaya's design philosophy: agents should not be loose chatbots with unlimited powers. They need scoped tools, clear permissions, secure credentials, cost tracking, audit logs, data-quality monitoring, and human approval where risk is high. The product applies trading-grade discipline to a general automation platform. ## Core capabilities - **275+ agentic tools** across web, code, data, ops, trading, communication, files, cloud, social, RAG, and MCP bridge - **100+ specialized subagents** with prebuilt crews for research, analysis, execution, and review workflows - **Bring-your-own model**: Anthropic Claude, OpenAI GPT, Google Gemini, Ollama, LM Studio, Zhipu, Claude Code CLI, OpenAI Codex CLI - **Per-workflow RAG**: isolated vector stores with hybrid BM25 + dense retrieval, scaling to 500 GB on the top tier - **Secure connectors**: AES-256-GCM envelope encryption, Infisical-managed secrets, per-user credential isolation - **Full observability**: pipeline run logs, tool-call traces, model invocation cost and token tracking, error reasons - **Human-in-the-loop**: per-tool HITL gates, approval queue, scoped permissions and roles - **Multi-tenant**: project-scoped RBAC, per-project credential vaults, isolated execution environments - **Triggers**: cron, webhooks, SSO, programmatic SDK, manual run ## Trading engine ("Melaya Engine") A Rust-native trading core that exposes venues, fills, and live market state back to every agent in the framework. Hot-path dispatch is 420 ns, measured in CI. Coverage: - **99 markets** across 12+ centralized exchanges: Binance, OKX, Bybit, Kraken, Gate, HTX, Bitget, MEXC, KuCoin, Woo, BitMEX, and more. Spot, perpetuals, and futures on the same execution layer. - **6 prediction-market adapters**: Polymarket, Kalshi, Drift, SX Bet, Azuro, Overtime. Event discovery, CLOB/AMM orderbooks, trades, OHLCV, and live WebSocket streams through the same ingress as every spot venue. - **Backtests + live simulation + dry runs** for any strategy expressed through the agent framework. - **Per-account routing** with isolated risk and Infisical-managed keys. ## Tier model Canonical tier names: `sandbox`, `forge`, `bastion`, `citadel`. These map to the platform-account billing tiers AND to the engine rate-limit buckets — same key, same enforcement everywhere. | Tier | Price | WS connections | streams/conn | frame interval | OB depth | REST req/min | session cap | | ------- | ----------- | -------------- | ------------ | -------------- | -------- | ------------ | ----------- | | sandbox | $0 | 2 | 1 | 500ms | L10 | 60 | 24h | | forge | $49/mo | 10 | 3 | 200ms | L50 | 600 | 24h | | bastion | $129/mo | 20 | 5 | 100ms | L100 | 3000 | 24h | | citadel | custom | 100 | 20 | real-time | full | unlimited | unlimited | Tier governs throttle + connection cap, NOT feature access at the route layer (every endpoint accepts every tier; the engine pushes frames at the tier's configured rate and rejects new connections beyond the cap with close code 4029). --- # Engine API Reference Three surfaces: 1. **REST** — `https://api.melaya.org/api/v1/...` for one-shot reads + trading 2. **WebSocket** — `wss://wss.melaya.org/ws/` for live streaming 3. **Agent Python tools** — importable inside Melaya pipelines; thin wrappers that call the REST surface server-side All three respect the same auth model and tier semantics. ## Authentication Three credential types, each scoped to a distinct surface: - **`mk_*` API keys** — prefixed `mk_`, 34 chars, base16. Generated once via `POST /api/v1/private/api-key` (only displayed once, regenerate to rotate). Pass as `?apiKey=mk_...` query string OR `X-Api-Key: mk_...` HTTP header. Used by: REST market-data endpoints AND public WS streams (orderbook, ohlcv, public-trades, ticker, liquidations). - **JWT session cookie** — set by `POST /api/v1/app/auth.login` (with optional MFA via `auth.verifyMfa`). Auto-sent by the browser; also accepted as `Authorization: Bearer `. Used by: every per-user route (`/api/v1/private/*`, `/api/v1/strategies/*`, `/api/v1/app/*`) AND first-party browser WS connections (the wsAuth layer now extracts the JWT from cookies so dashboard-driven WS frames are attributed to the right user for the usage meter). - **WS tickets** — short-lived JWTs minted by `POST /api/v1/private/private-ticket` with `stream:"private"` or `stream:"strategies"` claims. Pass as `?wsTicket=` on the WS URL. Required for `/ws/private` and `/ws/strategies` (the only WS endpoints that DON'T accept a raw `mk_*` key — they need a per-stream authorization claim that a long-lived key can't carry). - **Internal HMAC** — `X-Melaya-User-Id` + `X-Melaya-User-Sig` (`hmac_sha256(INTERNAL_API_SECRET, user_id)`), OR `Authorization: Bearer $INTERNAL_API_SECRET`. Used only by engine/worker callbacks; not for end-user code. ## WebSocket close codes Only four codes are emitted by the engine. Anything else is a TCP / load balancer issue, not a Melaya-side reject. - **4000** `max_session_reached` — 24h session cap (sandbox/forge/bastion). Reconnect cleanly with the same key. - **4001** `invalid_api_key` — bad or revoked key. Regenerate; do NOT retry. - **4003** `tier_insufficient` — this stream requires a higher tier than your key carries. Upgrade tier or pick an allowed feed. - **4029** `rate_limited` — concurrent connection cap exceeded for your tier. Back off ≥5s; existing connections are unaffected. ## Reconnection contract Exponential backoff: `250ms → 500ms → 1s → 2s → 4s`, cap `30s`, plus `0-20%` jitter. Server emits a `{type:"ping",ts}` frame every 25s if the connection is idle; respond with `{type:"pong",ts}` or send any other frame (subscribe, unsubscribe, ping) to bump the activity timer. The receive-timeout watchdog closes the socket after 35s of silence. --- # REST API Reference Base URL: `https://api.melaya.org`. Response convention: `{ok: bool, ...}` on every `/api/v1/market/*` route. Some `/api/v1/private/*` routes return the data directly (no `ok` wrapper); each entry below states which. ## REST · Market data — public + mk_* api key Read-only price/depth/derivatives data. No exchange credentials required — the Melaya engine multiplexes a single WS connection per venue and serves clients from in-process cache. ### GET /api/v1/market/list-exchanges Auth: public. Returns `{ok, count, exchanges: string[], asOfMs, source: "native"|"static"}`. Use to enumerate supported exchange IDs before choosing one for downstream calls. ### POST /api/v1/market/instruments Auth: public. Input: `{exchange, market?: "SPOT"|"FUTURES", params?}`. Returns the symbol catalog for the venue, cached server-side with cold-cache REST fallback. ### POST /api/v1/market/tickers Auth: public. Input: `{exchange, symbols?: string[]}`. Returns `{ok, exchange, symbols, count, tickers: {[sym]: ticker}}`. Reads the WS-warmed cache; falls back to REST `fetch_tickers` if any requested symbol is cold. ### GET /api/v1/market/ticker Auth: mk_* api key. Query: `exchange, symbol`. Returns `{ok, exchange, symbol, ticker | null}`. The cache only warms when a WS ticker subscription is active for that (exchange, symbol); without one, `ticker:null` is correct (not an error). ### POST/GET /api/v1/market/orderbook Auth: public (POST) or mk_* (GET). Input: `{exchange, symbol, limit?}`. Returns CCXT-shape `{ok, orderbook: {bids: [[p,q],...], asks: [...]}}`. ### POST/GET /api/v1/market/ohlcv Auth: public (POST) or mk_* (GET). Input: `{exchange, symbol, timeframe?, limit?}`. Returns `{ok, candles: [[ts,o,h,l,c,v],...]}`. Supported timeframes: any of `1m 3m 5m 15m 30m 1h 2h 4h 6h 8h 12h 1d 3d 1w 1M` — the engine synthesises non-native timeframes from the largest supported divisor via O(n) streaming aggregation. ### POST /api/v1/market/ohlcv-multi Auth: public. Input: `{exchange, symbols: string[], timeframe?, limit?}`. Batch OHLCV. Returns `{ok, multiCandles: {[sym]: candles[]}}`. ### POST/GET /api/v1/market/public-trades (and /api/v1/market/trades) Auth: public (POST) or mk_* (GET). Input: `{exchange, symbol, limit?}`. Recent public trades, newest first. ### POST /api/v1/market/funding-rates Auth: public. Input: `{exchange, symbols?}`. Current funding rates per perp symbol. ### POST /api/v1/market/funding-rate-history Auth: public. Input: `{exchange, symbol, limit?}`. Historical funding rate snapshots for one symbol. ### POST /api/v1/market/funding-rate-history-multi Auth: public. Input: `{exchange, symbols: string[], limit?}`. Batch funding-rate history. Returns `{ok, multiHistory: {[sym]: [...]}}`. ### POST /api/v1/market/open-interest Auth: public. Input: `{exchange, symbols?}`. Current OI per symbol. ### POST /api/v1/market/open-interest-history Auth: public. Input: `{exchange, symbol, limit?}`. Historical OI for one symbol. ### POST /api/v1/market/open-interest-history-multi Auth: public. Batch OI history. ### POST /api/v1/market/liquidation-events Auth: public. Input: `{exchange, symbol?, hours?, limit?, sinceMs?}`. Returns persisted liquidation events from `cex.liquidation_events` — the chart-heatmap data source. Live events stream over `/ws/liquidations`. ### POST /api/v1/market/market-constraints / market-constraints-overview Auth: public. Returns contract specs per symbol (min/max order, tick size, step size, fee tier hint, etc.). ### POST /api/v1/market/pm-markets Auth: public. Input: `{exchange}`. Returns prediction-market venues (Polymarket, Kalshi, etc.) and their event surfaces. ### GET /api/v1/market/markets Auth: mk_* api key. Query: `exchange`. Returns `{ok, markets: [CcxtMarket]}` — the full instrument list with limits + precision. ### GET /api/v1/market/currencies Auth: mk_* api key. Query: `exchange`. Returns `{ok, currencies: {[code]: CcxtCurrency}}` — coin metadata, networks, withdraw fees. ### GET /api/v1/market/time Auth: mk_* api key. Returns `{ok, timestamp: int_ms}`. Server time; useful for client clock-skew detection. ### GET /api/v1/market/status Auth: mk_* api key. Query: `exchange`. Returns `{ok, status: {status: "ok"|"maintenance"|"shutdown", updated?: int_ms, eta?: int_ms, url?: string}}`. ### GET /api/v1/market/fees Auth: mk_* api key. Query: `exchange`. Returns the venue's published maker/taker tier schedule. ### GET /api/v1/market/liquidity Auth: mk_* api key. Query: `exchange`. Per-symbol liquidity metrics (spread, depth at +1%/+2%, 24h volume). ## REST · Trading — per-user (JWT cookie OR mk_* api key) These routes operate on the calling user's stored exchange credentials (Infisical-managed). The user MUST have added the relevant exchange credential via `accounts.addKey` first. ### GET /api/v1/private/keys Auth: JWT cookie / Bearer. Lists the user's stored CEX credentials (masked). Returns `{keys: [{id, apiKeyId, apiKey: "abc...****", exchange, label, market, slot, client}]}`. ### GET / POST / DELETE /api/v1/private/api-key Auth: JWT cookie / Bearer. - `GET` returns `{key: string|null}` — the user's PLATFORM mk_* key. - `POST` generates a new mk_* key (invalidates previous). Response is the only time the raw key is shown. - `DELETE` revokes every mk_* key the user owns. ### GET /api/v1/private/api-key/usage Auth: JWT cookie / Bearer. Query: `range=1h|today|7d|30d`. Returns: ```json { "totalRequests": int, "totalMessages": int, "totalBytes": int, "totalConnections": int, "daily": [{"date":"YYYY-MM-DD", "requests": int, "messages": int, "bytes": int, "connections": int}, ...], "series": {"": [{"bucket": "...", "requests": int, ...}, ...]}, "breakdown": { "channels": [{"key":"channel.api_private","label":"...","requests": int, ...}, ...], "features": [{"key":"feature.private.create-order", ...}, ...], "venues": [{"key":"venue.binance", ...}, ...] } } ``` WS frame attribution requires either an mk_* key in the URL or a JWT cookie at handshake. ### POST /api/v1/private/private-ticket (also GET form) Auth: JWT cookie / Bearer. Input: `{stream?: "private", exchange?, keyId?, symbol?}` (claims for the WS layer). Returns `{ok, wsTicket: string, expiresInMs: 120000}`. Use the returned `wsTicket` as `?wsTicket=` on `/ws/private` or `/ws/strategies`. ### POST /api/v1/private/warmup Auth: JWT cookie / Bearer. Input: `{exchange, keyId, market?: "spot"|"futures"}`. Pre-builds the napi `OrderSession` so the first order/cancel/balance call doesn't pay the cold-start cost. ### Per-account trading actions (`/api/v1/private/`) All take `{exchange, keyId?, ...action_specific}` and return `{ok: bool, ...}` or `{ok: false, error: string}`. - `POST /create-order` — `{symbol, side: "buy"|"sell", amount, price?, type?: "limit"|"market", reduceOnly?, stopPrice?, takeProfit?, stopLoss?, postOnly?, timeInForce?: "GTC"|"IOC"|"FOK", clientOrderId?}` - `POST /cancel-order` — `{orderId, symbol?}` - `POST /amend-order` — `{orderId, amount?, price?, ...}` - `POST /cancel-all-orders` — `{symbol?}` - `POST /balance` — `{}` → `{balances: {[coin]: {free, used, total}}}` - `POST /positions` — `{}` → `{positions: [...]}` (perps only) - `POST /open-orders` — `{symbol?}` → `{orders: [...]}` - `POST /orders` — `{symbol?, limit?}` → historical orders - `POST /my-trades` — `{symbol?, limit?}` → recent fills - `POST /my-trades-history` — `{symbol?, sinceId?, limit?}` → paginated - `POST /positions-history` — `{symbol?, sinceId?, limit?}` - `POST /leverage-tiers` — `{}` → `{tiers: {[sym]: tier[]}}` - `POST /set-leverage` — `{symbol, leverage: int}` - `POST /set-margin-mode` — `{symbol, marginMode: "isolated"|"cross"}` ## REST · Strategy lifecycle + backtest Strategies are long-running, docker-isolated trading containers managed by the platform. Each is owned by one user, runs from a docker image (`melaya-strategy-runner-rust:latest`), and reports status / executions / performance back to Postgres + Redis. ### GET /api/v1/strategies/list Fast metadata-only list (no performance) of the caller's strategies. Returns `{ok, count, strategies: [...]}`. ### POST /api/v1/strategies/sync Bulk sync — strategies + live performance metrics. Input: `{includeDeleted?}`. Returns `{ok, data: {ok, count, strategies, performanceByStrategy}}`. ### POST /api/v1/strategies/perf Batch performance with cursor pagination. Input: `{strategyIds, perfSinceMsByStrategy?, perfLimitPerStrategy?}`. ### POST /api/v1/strategies/executions / trades / logs Batch fetches keyed by `{strategyIds}`. Logs accepts `{sinceMs?, limitPerStrategy?}` and returns a `moreCursor` per strategy. ### POST /api/v1/strategies Create new strategy. Input: `{name, strategyType: "long_short_1m" | "arvm" | "limit_schedule" | "custom" | ..., exchange, symbol, market?: "spot"|"futures", params: object, apiKeyId?, dryRun?, projectName?}`. Returns `{ok, strategyId, status: "creating"}` and spawns the docker container in the background (~1-3s). ### GET / DELETE /api/v1/strategies/:id - `GET` — full strategy detail + perf - `DELETE` — soft-delete (sets `deletedAt`; can be undeleted by admin) ### POST /api/v1/strategies/:id/pause | resume | stop | update-params | switch-instrument | rebalance | rebalance-stop Lifecycle actions. Each returns `{ok, status?}`. - `update-params` — `{params: object}` - `switch-instrument` — `{exchange, symbol, market?}` - `rebalance` — `{targetAllocation?: object}` ### GET /api/v1/strategies/:id/performance | executions | trades | logs | status Per-strategy reads. `logs` supports `?sinceMs=&limitPerStrategy=` and returns `moreCursor`. ### AI optimization — `GET /:id/ai-opt/status`, `POST /:id/ai-opt/approve`, `POST /:id/ai-opt/stop` The platform can run LLM-driven parameter optimization against a strategy's backtest matrix. `approve` accepts `{jobId}` and applies the suggested params. ### Backtest - `POST /api/v1/private/backtest/start` — `{strategyType, exchange, symbol, market?, params, backtestConfig: {since_ms, until_ms, timeframe, initial_equity, fee_bps?, slippage_bps?, sweep_type?: "single"|"grid"|"random", param_ranges?, random_samples?, ...}}` → `{ok, jobId}`. Job runs async; poll status. - `GET /api/v1/private/backtest/jobs/:jobId` → `{ok, status: "pending"|"running"|"done"|"error", ...}` - `GET /api/v1/private/backtest/results/:jobId` → metrics: `netPnl, sharpe, maxDrawdown, winRate, totalTrades, ...` - `GET /api/v1/private/backtest/trades/:jobId` → per-trade detail - `GET /api/v1/private/backtest/sweep/:parentId` → `{ok, sweeps: [...], bestJobId?}` for grid/random sweeps - `GET /api/v1/private/backtest` → `{ok, jobs: [...]}` (list, paginated) - `GET /api/v1/private/backtest/favorites` → user's starred runs - `GET /api/v1/private/backtest/funding-range` — `{exchange, symbol}` → historical funding rate range, useful as a strategy-param guardrail - `DELETE /api/v1/private/backtest` (bulk) or `/:jobId` (single) - `POST /api/v1/private/backtest/:jobId/cancel` — kill a running job - `PUT /api/v1/private/backtest/:jobId/favorite` — `{isFavorite: bool}` ## REST · Strategy docs + custom strategy templates - `GET /api/v1/private/strategy-docs` → all strategy types + docs - `GET /api/v1/private/strategy-docs/:strategyType` → one type - `PUT /api/v1/private/strategy-docs/:strategyType` — `{docs: string}` - `DELETE /api/v1/private/strategy-docs/:strategyType` - `GET /api/v1/private/custom-strategies` → user's custom templates - `GET /api/v1/private/custom-strategies/:name` → `{code, type}` - `PUT /api/v1/private/custom-strategies/:name` — `{code, type?}` - `DELETE /api/v1/private/custom-strategies/:name` ## REST · Platform account (tRPC procedures on `/api/v1/app/*`) Cookie-session only. Body: tRPC JSON shape (`{json: }` for batched calls; unwrapped JSON for single calls). ### auth.* - `auth.login` — `{username, password}` → `{requiresMfa: false, token, user}` OR `{requiresMfa: true, challengeToken, challengeTtlSeconds: 300}`. - `auth.verifyMfa` — `{challengeToken, code}` → `{token, user}`. - `auth.register` — `{email, password, username, referralCode?}` - `auth.verifySignup` — `{email, token}` - `auth.resendVerification` — `{email}` - `auth.me` — `{}` → `{user}` (current session) - `auth.check` — `{}` → `{user, isValid}` - `auth.changePassword` — `{oldPassword, newPassword}` - `auth.forgotPassword` — `{email}` - `auth.resetPassword` — `{email, token, newPassword}` - `auth.loginGoogle` — `{code, codeVerifier?}` (OAuth) - `auth.logout` — `{}` ### accounts.* - `accounts.listKeys` — `{}` → `{keys: [...]}` (the CEX credentials the user has stored) - `accounts.addKey` — `{exchange, label?, apiKey, secret, passphrase?}` - `accounts.removeKey` — `{id}` - `accounts.credits` — `{}` → `{credits: number}` - `accounts.feesMapping` — `{}` → `{[client]: profile}` - `accounts.updateFees` — `{client, profile}` ### billing.* - `billing.subscription` — `{}` → `{tier, stripe_customer_id?, status}` - `billing.createCheckout` — `{priceId, successUrl, cancelUrl}` → `{sessionUrl}` - `billing.createPortal` — `{returnUrl}` → `{portalUrl}` ### market.* (tRPC mirror of the REST market endpoints — preserved for the FE client; servers should prefer the REST routes) - `market.tickers` — `{exchange, symbols?}` - `market.orderbook` — `{exchange, symbol, limit?}` - `market.ohlcv` — `{exchange, symbol, timeframe?, limit?}` ### bugs.* - `bugs.create` — `{title, description, priority?: "low"|"medium"|"high"}` - `bugs.listMine` — `{limit?, offset?}` - `bugs.get` — `{bugId}` - `bugs.addComment` — `{bugId, body}` - `bugs.listNotifications` — `{}` → `{notifications: [...]}` - `bugs.markNotificationsRead` — `{notificationIds: string[]}` ### Bug attachments (multipart over plain Express, not tRPC): - `POST /api/v1/bugs/:id/attachments` — multipart `files[]` (max 5 files, 10 MB each, 40 MB total per bug) - `GET /api/v1/bugs/:id/attachments/:attId` — download - `GET /api/v1/bugs/:id/archive.zip` — zipped attachments (closed bugs) - `GET /api/v1/bugs/_health` — admin storage diagnostic ## REST · Internal (operator-only, HMAC) Used by the engine + worker; not part of the user-facing surface. Listed for completeness only. - `POST /api/v1/internal/env-handle/:token` — env-var handle resolver (LLM relay callbacks) - `POST /api/v1/internal/apikey-resolve` — `{apiKey}` → identity - `POST /api/v1/internal/backtest-optimize/callback` — optimizer incremental updates - `POST /api/v1/internal/build-pipeline/llm-relay/callback` — agent studio LLM relay - `POST /api/v1/internal/optimizer/llm-relay` — optimizer outbound LLM ## REST · Utilities - `GET /api/health` → `{status:"ok", uptime}` (no auth) - `GET /api/v1/public/catalog-counts` → `{catalogItems, exchanges, ...}` - `POST /api/webhooks/stripe` — Stripe-signed event handler --- # WebSocket Streams Reference Base URL: `wss://wss.melaya.org`. Five public streams (orderbook, ohlcv, public-trades, ticker, liquidations; `/ws/trades` is an alias of `/ws/public-trades`) plus one private multiplex (`/ws/private`) and one strategies feed (`/ws/strategies`). ## Auth contract (all WS routes) - Public streams (`/ws/orderbook`, `/ws/ohlcv`, `/ws/public-trades`, `/ws/ticker`, `/ws/liquidations`): pass `?apiKey=mk_*` OR connect from a first-party browser origin with a valid JWT session cookie. The wsAuth layer accepts both; the JWT cookie path drives FE dashboard WS usage. - Private mux (`/ws/private`) + Strategies (`/ws/strategies`): require a short-lived JWT ticket. Mint via `POST /api/v1/private/private-ticket` and pass as `?wsTicket=`. ## Inbound control protocol (shared across public streams) - `{type:"subscribe", exchange, symbol, market?, chain?, poolAddress?, instrumentKey?, ...feed-specific}` — add a subscription - `{type:"unsubscribe", ...same keys}` — drop one - `{type:"ping"}` or bare `"ping"` string → server replies `{type:"pong", ok:true, ts}` - Server sends `{type:"ping",ts}` every 25s when idle; client should reply with `{type:"pong"}`. Subscribe ack: `{type:"subscribed", ok:true, exchange_id, symbol, market, chain, poolAddress, instrumentKey, ts}`. ## /ws/orderbook Auth: mk_* OR cookie. Min tier: sandbox. URL params: `exchange` (or csv `exchanges`), `symbol` (or csv `symbols`), `market`, `limit`, `delta` (default false), `chain`, `poolAddress`, `instrumentKey`. Default frames are full snapshots. `delta=true` switches to delta-encoded frames with a monotonic `seq` for ordering. `nonce` mirrors the exchange sequence number when available. Frame: ```json { "type": "orderbook", "ok": true, "exchange_id": "binance", "symbol": "BTC/USDT", "market": "SPOT", "limit": 20, "chain": "", "poolAddress": "", "instrumentKey": "", "timestamp": 1774994319865, "datetime": "2026-04-30T12:18:39.865Z", "nonce": 7634212901, "orderbook": { "bids": [[68249.06, 0.76622], [68249.05, 0.02227]], "asks": [[68249.07, 0.97791], [68249.08, 0.00008]] }, "warnings": [] } ``` ## /ws/public-trades (alias: /ws/trades) Auth: mk_* OR cookie. Min tier: sandbox. URL params: `exchange`/`exchanges`, `symbol`/`symbols`, `market`, `chain`, `poolAddress`, `instrumentKey`. BATCHED — up to 80 trades per frame. First frame after subscribe has `snapshot:true` (60 most-recent trades); subsequent frames are live deltas only. Per-topic dedup (the same trade never appears twice). `seq` is monotonic per topic. Frame: ```json { "type": "public_trades", "ok": true, "exchange_id": "binance", "symbol": "BTC/USDT", "market": "SPOT", "timestamp": 1774994319090, "seq": 1834, "trades": [ {"id": "98765432", "timestamp": 1774994319090, "datetime": "2026-04-30T12:18:39.090Z", "side": "sell", "price": 68249.05, "amount": 0.00112, "cost": 76.44, "takerOrMaker": "taker"} ] } ``` ## /ws/ohlcv Auth: mk_* OR cookie. Min tier: forge. URL params: `exchange`, `symbol`, `timeframe` (any of the 15 supported), `market`, `limit`, `since` (optional ms). First frame after subscribe has `bootstrap:true` and backfills `limit` candles. Subsequent frames carry only the live candle plus any candles that have closed since the last frame. The `live` block is the sub-candle tick (bid/ask/mid at nanosecond precision). No `is_closed` field — a candle is closed when a later candle appears with a greater `ts`. Frame: ```json { "type": "ohlcv", "ok": true, "bootstrap": false, "exchange_id": "binance", "symbol": "BTC/USDT", "market": "SPOT", "timeframe": "1m", "since_ms": 1774994000000, "limit": 200, "last_candle_ts": 1774994280000, "candles": [[1774994280000, 68276.27, 68276.28, 68243.85, 68243.86, 9.58448]], "live": {"mid": 68243.86, "bid": 68243.85, "ask": 68243.86, "ts_ns": 1774994319865112293}, "seq": 412, "sent_ts": 1774994319870 } ``` ## /ws/ticker Auth: mk_* OR cookie. Min tier: forge. URL params: `exchange`/`exchanges`, `symbol`/`symbols`, `market`, `chain`, `poolAddress`, `instrumentKey`. Frame fires only when `ts_ns` advances (no duplicate frames). NOT supported on `apex`, `p2b`, `coincheck`, `foxbit` — those handlers do not emit normalized ticker frames; the route rejects subscribes for those venues with `{error: "ticker_unsupported_on_venue"}`. Use `/ws/orderbook` or `GET /api/v1/market/ticker` for those venues. Frame: ```json { "type": "ticker", "ok": true, "exchange_id": "binance", "symbol": "BTC/USDT", "market": "SPOT", "timestamp": 1774994319865, "datetime": "2026-04-30T12:18:39.865Z", "seq": 5621, "bid": 68243.85, "ask": 68243.86, "last": 68243.86, "bidVolume": 0.76622, "askVolume": 0.97791, "baseVolume": 18943.21, "quoteVolume": 1294825110.0, "high": 69120.0, "low": 67980.0, "open": 68110.0, "close": 68243.86, "previousClose": 68112.0, "change": 133.86, "percentage": 0.197, "vwap": 68312.10, "average": 68176.93, "info": {"recv_ns": 1774994319865112293, "latency_ms": 4} } ``` ## /ws/liquidations Auth: mk_* OR cookie. Min tier: bastion. URL params: `exchange` (optional — omit for firehose across all venues). Native WS on Binance USDM/COIN-M, Bybit linear/inverse, OKX, Gate, BitMEX, Bitget v3 UTA, dYdX v4. Each frame is the engine's normalised liquidation event forwarded as-is (no envelope wrapping). Heatmap- backing rows are also persisted to `cex.liquidation_events` so `POST /api/v1/market/liquidation-events` returns historical query results. Frame: ```json { "exchange": "binanceusdm", "symbol": "BTCUSDT", "side": "sell", "price": 65000.0, "quantity": 1.5, "notional": 97500.0, "market_type": "usdt", "timestamp": 1774994320456 } ``` ## /ws/private — single-socket multiplex of all private streams Auth: `?wsTicket=`. URL params: `wsTicket`, `exchange`, `market`, `chain?`, `limit?`. Open ONE connection per (exchange, market) pair; subscribe to MULTIPLE streams on it via inbound control frames. Stream names below are the `stream` value to send in `{type:"subscribe", stream:"", ...}`. On accept, server emits `{type:"hello", ok:true, trace_id, stream: "private", exchange_id, market, chain, limit, key_hash, role, ts}`. On each `subscribe`: ack `{type:"ack", stream, internal_stream, exchange_id, symbol, market, chain, limit, key_hash, ts}` → bootstrap `{type:"bootstrap", stream, snapshot:true, mode:"snapshot", source:"rest_bootstrap", method, data:[...], asOfMs, ts}` → live deltas `{type:"delta", stream, event_type, mode:"delta", source:"native_private_bus", data:[...], payload?, asOfMs, ts}`. ### stream:"watch-balance" (min tier bastion) Subscribe: `{type:"subscribe", stream:"watch-balance", params:{usdValues?:true}}`. With `usdValues:true` the engine multiplexes public-ticker subscriptions on the same socket and fuses price changes with balance changes, emitting enriched events with `usdValue` and `usdPrice` at nanosecond speed. Delta `data[]` shape: ```json [{ "timestamp": 1776425570351, "accountType": "SPOT", "totalUsd": 25345.72, "balances": [ {"asset":"BTC","free":0.25,"used":0,"total":0.25, "usdValue":24950.0,"usdPrice":99800.0}, {"asset":"USDT","free":395.72,"used":0,"total":395.72, "usdValue":395.72,"usdPrice":1.0} ] }] ``` ### stream:"orders" (min tier bastion) Subscribe: `{type:"subscribe", stream:"orders", symbol?, limit?:100}`. Covers open / partial / filled / cancelled / rejected. Delta `data[]` shape: ```json [{ "id":"28457921","symbol":"BTC/USDT","type":"limit","side":"buy", "price":65000,"amount":0.01,"filled":0,"remaining":0.01, "status":"open","timestamp":1775002341013 }] ``` ### stream:"my-trades" (alias: "fills") (min tier bastion) Subscribe: `{type:"subscribe", stream:"my-trades"}` (or `"fills"` — same internal channel). Delta `data[]` shape: ```json [{ "id":"98765432","orderId":"28457921","symbol":"BTC/USDT","type":"limit", "side":"buy","price":65000,"amount":0.01,"cost":650, "fee":{"cost":0.000001,"currency":"BNB"},"timestamp":1775002341013 }] ``` ### stream:"positions" (min tier citadel — futures / perps only) Subscribe: `{type:"subscribe", stream:"positions", symbol?}`. Spot venues reject with `{error:"BadRequest"}`. Delta `data[]` shape: ```json [{ "symbol":"BTC/USDT:USDT","side":"long","contracts":0.01, "entryPrice":65000,"markPrice":68035,"unrealizedPnl":30.35, "leverage":10,"liquidationPrice":58500,"margin":65, "timestamp":1775002341013 }] ``` ## /ws/strategies Auth: `?wsTicket=`. Min tier: bastion. Server pushes bootstrap on connect with every strategy you own + their container state + recent perf. Continuous live pushes of `strategy_status` / `strategy_perf` / `strategy_trade` events thereafter. Send `{type:"action", action: "pause"|"resume"|"stop"|"delete"|"rebalance", strategyId}` over the same socket to drive lifecycle without round-tripping through REST. Bootstrap frame: ```json { "type": "bootstrap", "ok": true, "trace_id": "...", "stream": "strategies", "tsMs": 1775002341013, "data": { "ok": true, "serverTimeMs": 1775002341013, "count": 2, "includePerf": true, "strategies": [{ "strategyId":"strat_…","name":"...","status":"running", "runtime":{"uptimeMs":...,"lastTickAt":...}, "container":{"exists":true,"state":"running","health":"healthy", "startedAt":...,"finishedAt":null,"exitCode":null} }], "performanceByStrategy":{"strat_…":[]}, "source":"ws_bootstrap_native","bootstrapMs":45 } } ``` Action-result frame: ```json { "type":"action_result","ok":true,"trace_id":"...", "action":"pause","strategyId":"strat_...", "result":{"ok":true,"status":"paused"},"tsMs":1775002341013 } ``` --- # Agent Python Tools For agents running inside Melaya pipelines (the agentic framework, not the public API consumer), three Python modules expose the same surface as the REST API in tool-call form. All three are importable from `shared.tools.melaya*` and pre-registered in every agent's toolkit by the agent factory (`shared.runtime.agent_factory`). Each tool returns either a JSON string (the result envelope) or a `ToolResponse` object the agent's ReAct loop can consume directly. Auth: pipelines run server-side and the runner injects a JWT for the owning user via env vars (`MEL_PLATFORM_TOKEN`, `MEL_USER_ID`). The tools call the REST surface authenticated as the pipeline owner; nothing the agent emits escalates beyond the user's own tier or stored credentials. ## shared.tools.melaya — market data Market reads. No exchange credentials needed; all hit `/api/v1/market/*`. ``` melaya_funding_rates_latest() # cached latest snapshot melaya_funding_rates_history(exchange, symbol, limit=100) melaya_funding_rates_fetch(exchange, symbols) # one-shot REST melaya_funding_rate_history_multi(exchange, symbols, limit=100) melaya_open_interest_latest() melaya_open_interest_fetch(exchange, symbols) melaya_open_interest_history(exchange, symbol, limit=100) melaya_open_interest_history_multi(exchange, symbols, limit=100) melaya_cex_liquidations_latest(lookback=None) # recent persisted events melaya_ohlcv(exchange, symbol, timeframe="1m", limit=100, since_ms=None) melaya_ohlcv_multi(exchange, symbols, timeframe="1m", limit=100) melaya_orderbook(exchange, symbol, depth=20) melaya_tickers(exchange, symbols) # batch ticker snapshot melaya_public_trades(exchange, symbol, limit=100) melaya_liquidity(exchange, symbol, ...) # depth/spread metrics melaya_vwap_volume(exchange, symbol, ...) melaya_microstructure(exchange, symbol, ...) melaya_cross_exchange(symbol, exchanges) # arb / spread reads melaya_market_constraints(exchange, symbol) melaya_market_constraints_overview(exchange, symbols) melaya_exchanges_list() melaya_exchanges_for(symbol) melaya_instruments(exchange) melaya_mdd_pairs(exchange="binanceusdm") melaya_tokens_list(exchange="binanceusdm") melaya_price_history(...) melaya_prices_latest() melaya_market_snapshot(exchange="bybit", symbol="BTCUSDT") ``` ## shared.tools.melaya_exec — trading execution Per-user trading actions. Each call resolves the pipeline owner's stored credential for the requested exchange (Infisical-managed) and routes the order through the native engine. ``` melaya_list_accounts() → returns the user's stored CEX accounts (id, exchange, label, slot) melaya_place_order(exchange, symbol, side, amount, price=None, order_type="limit", reduce_only=False, stop_price=None, take_profit=None, stop_loss=None, post_only=False, time_in_force="GTC", client_order_id=None, key_id=None) → { ok, orderId, ... } HITL-gated by default — see the pipeline's `humanApprovalTools` config melaya_cancel_order(exchange, order_id, symbol=None, key_id=None) melaya_cancel_all_orders(exchange, symbol=None, key_id=None) melaya_get_open_orders(exchange, symbol=None, key_id=None) melaya_get_account_balance(exchange, key_id=None) melaya_get_positions(exchange, key_id=None) melaya_set_leverage(exchange, symbol, leverage, key_id=None) melaya_get_orders(exchange, symbol=None, limit=100, key_id=None) melaya_get_my_trades(exchange, symbol=None, limit=100, key_id=None) ``` ## shared.tools.melaya_strat — strategy + backtest Strategy lifecycle and backtest control. The same surface as the REST `/api/v1/strategies/*` and `/api/v1/private/backtest/*` routes, wrapped for ReAct-style agent calls. Backtest: ``` melaya_backtest_start(strategy_type, exchange, symbol, since_ms, until_ms, timeframe="1m", initial_equity=10000, fee_bps=10, slippage_bps=2, max_drawdown_halt_pct=None, params=None, dry_run=False, sweep_type="single", param_ranges=None, random_samples=50) → { ok, jobId } melaya_backtest_status(job_id) melaya_backtest_results(job_id) melaya_backtest_trades(job_id, limit=500, offset=0) melaya_backtest_sweep_results(parent_job_id, objective="sharpe", limit=200) melaya_backtest_list(limit=50, offset=0) melaya_backtest_delete(job_id) melaya_backtest_cancel(job_id) melaya_backtest_favorite(job_id, favorited=True) melaya_backtest_favorites(limit=200) melaya_backtest_funding_range(exchange, symbol) melaya_get_strategy_doc(strategy_type) ``` Strategy lifecycle: ``` melaya_strategy_create(name, strategy_type, exchange, symbol, market="futures", params=None, api_key_id=None, dry_run=False, project_name=None) → { ok, strategyId, status: "creating" } melaya_strategy_list() melaya_strategy_status(strategy_id) melaya_strategy_pause(strategy_id) melaya_strategy_resume(strategy_id) melaya_strategy_stop(strategy_id) melaya_strategy_update_params(strategy_id, params) melaya_strategy_switch_instrument(strategy_id, exchange, symbol, market="futures") melaya_strategy_performance(strategy_id, limit=500) melaya_strategy_executions(strategy_id) melaya_strategy_trades(strategy_id) melaya_strategy_logs(strategy_id) melaya_strategy_delete(strategy_id) melaya_strategy_rebalance(strategy_id, target_base_pct=50.0, max_notional_usd=30.0) ``` --- # Where to dive deeper - **Landing page** — https://melaya.org - **Documentation** — https://melaya.org/docs - **Pricing** — https://melaya.org/#pricing - **Capabilities matrix** — https://melaya.org/#capabilities - **Trading engine details** — https://melaya.org/#engine - **Founding team** — https://melaya.org/#team - **Privacy policy** — https://melaya.org/legal/privacy - **Terms of service** — https://melaya.org/legal/terms - **Security overview** — https://melaya.org/legal/security - **Discord community** — https://discord.gg/2BBMUUdnkj - **LinkedIn** — https://www.linkedin.com/company/melaya/ - **Founder LinkedIn** — https://www.linkedin.com/in/antoine-roche/ # Contact - General: info@melaya.org - Sales / Citadel inquiries: info@melaya.org - Founder: antoine.roche@melaya.org - Discord handle (founder): tokano1383 - Telegram handle (founder): @rocheantoine # Crawler notes This file is an experimental [llms.txt](https://llmstxt.org) summary intended for AI training and retrieval crawlers. It is plain Markdown and stable across releases. The API reference sections (REST, WebSocket, Agent Python Tools) are verified 1:1 against the implementation in `server/src/index.ts`, `server/src/wsFeeds/*.ts`, and `melayaAgents/shared/tools/melaya*.py`. When in doubt, the implementation wins. The full robots policy is at https://melaya.org/robots.txt. The sitemap is at https://melaya.org/sitemap.xml. Last updated: 2026-05-15