API reference

Every AI Market Maker endpoint with the permission it gates on, the WebSocket subscription contract and its event streams, the tables the addon writes, and the response conventions that catch integrators out.

4 min readUpdated 3 August 2026api, endpoints, websocket, permissions, tables

One API surface. Everything lives under /api/admin/ai/market-maker and every route carries an explicit permission — there is no user-facing endpoint in this addon.

Conventions that will catch you out

The platform pins the HTTP status at 200 and puts the real outcome in the body. Read the body, always. A client that branches on the status code will render a green toast over a failed deposit — which is exactly what the admin screens themselves did before v6.1.5, on pool deposits, withdrawals, rebalance calculations, configuration saves and deletes.

Amounts are DECIMAL(30,18) columns and arrive from mysql2 as strings. Coerce before arithmetic and before comparison: "9" >= "10" is true as text, which is how a market with a 9–10 price range became uneditable and how a market could be reported as over its volume budget.

priceEngineState is a JSON column. Production MySQL returns it already parsed; local MariaDB returns a string. The model handles both, but a direct database consumer must too.

entropySeed is the secret 64-bit seed that generates a market's entire future price path — anyone holding it can compute where the price will be, which on a market that settles binary options is the whole game. priceEngineState reveals the engine's internal position.

Both are removed by the model's serialiser and excluded at the query on the list endpoint, because the list route returns model instances that the response encoder walks itself, so the serialiser never ran there. If you build your own query, exclude them yourself.

Markets

List market makers, filtered and paged
Create a market maker. Starts STOPPED with an empty pool and six paused bots
One market maker with its pool, bots and market
Update the core configuration
Delete. Stops the market, cancels its orders, cascades to pool, bots and history
Lifecycle: START, PAUSE, RESUME or STOP
Move the target price. Must stay inside the configured range
Set the directional bias and its strength
Set the price mode, external symbol and correlation strength
Set base volatility, phase multiplier and momentum decay
Read the current phase and its schedule
Force a phase transition, bypassing the normal rules
Zero the daily volume and per-bot trade counters immediately

Creation requires marketId, targetPrice, priceRangeLow and priceRangeHigh. Everything else falls back: aggressionLevel to MODERATE, maxDailyVolume to 1000000, volatilityThreshold to 5, pauseOnHighVolatility to true and realLiquidityPercent to 20. Note that the create wizard sends its own values for all of those, so the API defaults only apply to a direct caller.

Valid status transitions are STOPPED to START, ACTIVE to PAUSE or STOP, and PAUSED to RESUME or STOP. Anything else is refused with the valid actions listed.

engineAttached is false when this process is not the one holding engine leadership. In that case a START is recorded and the message says the engine will begin on the next cycle, rather than claiming success.

Every one of the configuration routes applies its change to the running market, not just to the database row. Before v6.1.5 they wrote the row, reported success, and left the running market behaving exactly as before until the next full reload — up to 24 hours.

Bots

List a market's bots with their statistics
One bot in full, with performance and recent trades
Update trading configuration
Set status: ACTIVE, PAUSED or COOLDOWN

config accepts riskTolerance (0.1–1), tradeFrequency (HIGH/MEDIUM/LOW), avgOrderSize (above 0), orderSizeVariance (0.1–0.5), preferredSpread (above 0) and maxDailyTrades (above 0). Every out-of-range value comes back as a 400 naming the range it needed to be in, and the response reports changesApplied — a count of the fields that actually differed.

status accepts cooldownMinutes alongside COOLDOWN, defaulting to 15. Any other status clears the expiry. Activating a bot while its market maker is not ACTIVE is refused.

There is no create-bot or delete-bot endpoint. The roster of six is fixed at market creation.

Pool

Read the pool with its balances and P&L
Read the calling admin's ecosystem wallet balances for this pair
Deposit from the admin wallet into the pool
Withdraw from the pool to the admin wallet. Refused while ACTIVE
Report or execute the trade a target ratio would require

deposit and withdraw take currency (BASE or QUOTE) and amount. Both accept an optional idempotencyKey so a retried submit settles once; without one the ledger key is unique per request, so two identical calls are two movements.

rebalance takes targetRatio (0–1, default 0.5), mode (REPORT by default, or EXECUTE) and maxSlippagePercent (EXECUTE only, default 2). It refuses an ACTIVE market in both modes. EXECUTE answers 409 rather than partially filling when the real book cannot cover the move within the slippage limit.

Analytics

Fleet overview: TVL, volume today against budget, quoting states, engine status
Performance over 1h, 24h, 7d or 30d
P&L: daily, weekly, monthly, all-time, ROI and the fill breakdown
Trade history, filterable by date range and bot

The overview returns quoting — five counts that add up to the active market count — plus blockers per market naming the closed gate, band and bandPosition, inventory skew, and an engine block carrying isLeader and leaderArbitratedBy. quoteCurrency is null when your markets do not share a quote asset, and the client must then print the totals without a unit.

total24hVolume sums ACTIVE markets only; volumeToday sums every market whatever its status. Neither is a rolling day. recentTradeCount genuinely is.

performance returns truncated when the period held more rows than it will read, and its currentPrice is the live price — the running instance first, then the persisted checkpoint, and only then the target.

pnl returns a ledger block stating which table the period figures come from and when that ledger begins, because the all-time figure covers a longer span.

Emergency

Halt every market maker, pause every bot, disable the global switch
Clear the risk circuit breaker

stop accepts reason and cancelOpenOrders (default true). It returns marketsStopped, botsStopped, ordersCancelled, the reason it recorded and a timestamp. Send a real reason — it is written onto every market's history entry and cannot be reconstructed afterwards.

reset-breaker returns engineRunning, wasTripped and tripReason rather than a bare success, so a "cleared" response on a stopped engine cannot be mistaken for a fix.

WebSocket

Real-time market maker updates

Event-driven, not polled. Subscribing sends a full snapshot once; after that, frames arrive only when something happens.

{ "action": "SUBSCRIBE", "payload": { "marketMakerId": "…" } }

UNSUBSCRIBE takes the identical payload. Disconnecting removes every subscription the client held.

Stream Carries
ai-market-maker-data The initial snapshot: configuration, market, pool, every bot, the last 20 bot trades and the last 20 history entries
ai-market-maker-event One event, typed TRADE, ORDER, STATUS_CHANGE, BOT_UPDATE, POOL_UPDATE, BOT_ACTIVITY or ERROR

BOT_ACTIVITY carries what a bot is doing right now — AI_TRADE, REAL_ORDER_PLACED, ORDER_CANCELLED, ANALYZING, WAITING or COOLDOWN — with the side, price, amount and counterparty bot where they apply.

The snapshot degrades gracefully: if Scylla or the history query fails, the frame still arrives with the parts that succeeded and an errors array naming what did not. Only a missing market maker aborts it.

Events are raised by the engine, which runs in exactly one process. Treat the socket as an accelerator rather than the source of truth — the admin screens poll alongside it for the same reason.

Tables

MySQL:

Table Holds
ai_market_maker The configuration rows. marketId is unique, so one per ecosystem market
ai_market_maker_pool One pool per market maker: balances, capital basis, TVL and both P&L figures
ai_bot The six bots per market, with their configuration and lifetime real-fill statistics
ai_market_maker_history The immutable trail. Per-trade rows are pruned on the retention setting; lifecycle rows never are
ai_market_maker_engine_lease The database arbiter for engine leadership

ScyllaDB, in the Ecosystem keyspace:

Table Holds
ai_bot_orders Orders the bots placed, keyed by market and creation time
ai_bot_trades The bot tape, in 1e18 fixed point
ai_bot_real_trades Per-fill P&L against real users, in plain decimals. The source of the daily, weekly and monthly figures

Deleting a market maker cascades to its pool, its bots and its entire history.