{"openapi":"3.1.0","info":{"title":"Cubicle API","version":"1.0.0","summary":"The Operator Deck's gates and skills, its two MCP surfaces, and the open tape they read.","description":"The Cubicle Operator Deck's API: the studio's gates (read a trader's own rule into its seven parts, forge it into a deterministic machine, rehearse it over the real tape against a random null, have Jev grade its fidelity), the skill bank, the plan rail, the deck's own book routes and the observability plane. Every answer is read from the venue, the tape or the deck's own ledgers, never from a model's words; a rehearsal is evidence about the past, never a forecast.\n\nTwo MCP servers serve agents. The deck's own, POST /mcp (and one endpoint per seller at /mcp/{seller}), lists the tools in x-mcp.tools and acts as the signed-in principal. The Taifoon coordination layer's, https://coord.taifoon.dev/mcp (OpenAPI https://coord.taifoon.dev/v1/openapi.json), is where Cubicle's sellers are listed and hired: who is listed, on what terms, and the judge's verdict. The seller cards at /cards/{seller} are the ERC-8004 agentURI and the layer's card in one document.\n\nTrading hires are designed in the spec book (chapters 24 to 26): the job classes trading.mandate.v1 (vault venues, four checks, cover up to the cap) and trading.mandate.external.v1 (the user's own account at an external venue, checks 1 to 3, a hire fee only), and a standing mandate as a series of epoch jobs, one job per settlement epoch with its own digest, ruling and payment. The classes are served by the coordination layer; the series routes are a design and are not on this deck.\n\nA Cubicle key is minted once, by the person, at POST /join (https://cubicle.algotrada.com/deck/join in a browser), never by a server. An MCP client without a key is sent there out of band (URL-mode elicitation for a 2026-07-28 client, structured error data for an older one) and adds the key's session as its bearer. Limits verified from the code: 3 principals a day per client and 500 a day for everyone (join); 10 refused keys in 5 minutes from one address rests the login door for the rest of the window.\n\nThe open tape the deck's tape tool reads is served by the venue, not the deck, and is documented at https://www.taifoon.io/clob/openapi.yaml (x-related.openTape).","contact":{"name":"Cubicle","url":"https://cubicle.algotrada.com/deck"},"license":{"name":"Proprietary API"}},"externalDocs":{"description":"The Taifoon venue's OpenAPI document: the order book, the tape and the sessions the deck stands on.","url":"https://www.taifoon.io/clob/openapi.yaml"},"servers":[{"url":"https://cubicle.algotrada.com/deck/api","description":"the production deck"}],"tags":[{"name":"series","description":"A standing trading hire as a series of epoch jobs under one mandate: quote, open, read, open the next epoch, revoke (docs/spec-book/api/machine.json `series`)."},{"name":"identity","description":"A key minted once by the person, a session exchanged for it."},{"name":"plans","description":"The plan catalogue, claims, holder standing and the meter."},{"name":"mcp","description":"The seller cards and the two MCP endpoints of this deck."},{"name":"studio","description":"A trader's own rule: read, forged, rehearsed, graded."},{"name":"forge","description":"The forge wire and the trial stepper the studio stands on."},{"name":"tape","description":"The time machine's candles and replay sessions."},{"name":"records","description":"The deck's own records: history, roadmap, ladder, registry, the concluded lab."},{"name":"skills","description":"The skill bank."},{"name":"book","description":"The deck's own order and cancel routes, and read-only venue history."},{"name":"plane","description":"The observability plane and the hash-chained ledgers that ride it."},{"name":"chat","description":"The deck's chat lane through a fair queue."},{"name":"agent","description":"The agent rail: a venue replay driven against an operator's own decide() endpoint."},{"name":"typed","description":"The typed-decision lane, documented by its own contract."}],"paths":{"/v1/series/quote":{"get":{"tags":["series"],"operationId":"describeSeriesQuote","summary":"The venues a series may name, the limits and the quote body.","description":"Venues by the evidence the judge reads there (vault or external kind), the limits on epochs, price and markets, and an example body. Nothing is created.","security":[],"responses":{"200":{"description":"venues, limits, body.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"venues":{"type":"object"},"limits":{"type":"object"},"body":{"type":"object"}}}}}}}},"post":{"tags":["series"],"operationId":"quoteSeries","summary":"Quote a standing trading hire as a series of epoch jobs under one mandate (R01). Nothing is kept.","description":"The venue decides the kind (vault or external) and the class (trading.mandate.v1 or trading.mandate.external.v1); the layer prices one epoch (POST /v1/pools/quote, the one source of hire terms); the mandate is said in words; validUntil is the end of the series. Refusals the machine names: 422 no_lane (a venue the judge cannot read), 422 external_no_cover (cover above the seller's deposit on an external venue). A vault-kind series quotes but says why it cannot open yet (the TradingVault is a specification).","security":[],"requestBody":{"required":true,"description":"agent { chainId, agentId, seller }, venue, epochs, epoch_s?, price_usdc, caps { markets[], max_order_size, max_notional, max_position, allow_short? }, cover, vault?","content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"quote, per_epoch (the layer's terms), series_total_usdc, next.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"quote":{"type":"object"},"per_epoch":{"type":"object"},"series_total_usdc":{"type":"number"},"next":{"type":"string"}}}}}},"400":{"description":"bad_input: the problems name each field.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"no_lane | external_no_cover.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/series":{"get":{"tags":["series"],"operationId":"listSeries","summary":"This principal's series.","description":"Every series the signed-in principal opened, newest first, each with its next move.","security":[{"deckSession":[]}],"responses":{"200":{"description":"count, series[].","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"count":{"type":"integer"},"series":{"type":"array"}}}}}},"401":{"description":"No session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["series"],"operationId":"openSeries","summary":"Open a series from a confirmed quote (R02): the mandate in words, confirmed by the signed-in principal; the epochs cut with their paymentRef.","description":"External venues today (the hirer's own account at the venue; nothing on chain). paymentRef = sha256(series_id:index) per epoch. A vault-kind quote is refused (422 vault_not_built) until the TradingVault grants the mandate on chain.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"{ quote: the body POST /v1/series/quote took, from?: unix seconds }","content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"201":{"description":"series (state MANDATED), next.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"series":{"type":"object"},"next":{"type":"string"}}}}}},"401":{"description":"No session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"vault_not_built, or the quote's refusal.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The series could not be kept; nothing was opened.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/series/{id}":{"get":{"tags":["series"],"operationId":"readSeries","summary":"The series as it stands; the open epoch's hire is read from the layer and the series follows it (R04, R06, R10).","description":"The subscriber's own series only. On every read the open epoch's handshake is read from the layer; when the layer ended it the epoch ends here (R04), the last epoch or validUntil ends the series (R06), a stopped series with no open epoch ends (R10).","security":[{"deckSession":[]}],"responses":{"200":{"description":"series, hire?.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"series":{"type":"object"},"hire":{"type":"object"}}}}}},"401":{"description":"No session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such series of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["series"],"operationId":"revokeSeries","summary":"Revoke the mandate (R07 to R09): no new epoch opens; the open epoch, if any, still submits and is graded.","description":"The vault owner's remedy inside a series (on an external venue: the subscriber's). From MANDATED, EPOCH_OPEN or EPOCH_ENDED to STOPPED; a stopped series with no open epoch ends on its next read (R10).","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"{ revoke: true, why }","content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"moved, series.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"moved":{"type":"string"},"series":{"type":"object"}}}}}},"401":{"description":"No session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such series of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The series is already stopped or ended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/series/{id}/epochs":{"post":{"tags":["series"],"operationId":"openEpoch","summary":"Open the next epoch's job on the layer (R03, R05): a handshake with the seller and the unsigned openJob, as Cubicle's keeper.","description":"Steps 2 and 3 of the epoch plan with the deck's own Taifoon key: POST /v1/handshake with the seller (the layer checks it answers and records the opener), then POST /v1/settle via us (the unsigned openJob on the V4 devnet hook with our adapter). Steps 4 to 9 are the buyer's and the seller's wallets and the agent; the deck never signs. One epoch at a time, only when its window has begun and the mandate is valid.","security":[{"deckSession":[]}],"responses":{"201":{"description":"epoch, paymentRef, handshake_id, unsigned (the settle plan), series, next.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"moved":{"type":"string"},"epoch":{"type":"integer"},"paymentRef":{"type":"string"},"handshake_id":{"type":"string"},"unsigned":{"type":"object"},"series":{"type":"object"},"next":{"type":"string"}}}}}},"401":{"description":"No session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such series of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Not MANDATED or EPOCH_ENDED, or no epoch may open now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The layer refused the handshake; its answer is in `layer`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The deck's Taifoon key is not configured, or the series could not be written after the layer opened the epoch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/join":{"get":{"tags":["identity"],"operationId":"describeJoin","summary":"Describe self-service onboarding: the limits, the name rule and the optional wallet.","description":"Answers the limits the join route enforces (3 principals a day per client, 500 a day for everyone by default), the name rule and what the wallet field does. No data is created.","security":[],"responses":{"200":{"description":"The limits, the name rule and the wallet rule.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"limits":{"type":"object","properties":{"per_client_per_day":{"type":"integer"},"everyone_per_day":{"type":"integer"}}},"name_rule":{"type":"string"},"wallet":{"type":"string"}}}}}}}},"post":{"tags":["identity"],"operationId":"join","summary":"Mint a Cubicle principal and its key, once, for the person calling.","description":"The identity door mints the key and the deck returns it ONCE; the deck never logs it. The name is 3 to 32 characters of a-z, 0-9, - and _, lower-cased, and the reserved house names are refused. An optional 0x wallet becomes the principal's operator. Limits: 3 a day per client (a salted client address) and 500 a day for everyone by default. A server never mints a key for a person: an MCP client without a key is sent here (see x-mcp.keyless).","security":[],"requestBody":{"required":true,"description":"The principal's name and, optionally, its operator wallet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinRequest"}}}},"responses":{"201":{"description":"The principal and its key, shown once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinResponse"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That name is taken.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The name or the wallet failed its rule; the error names which.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"The per-client or the everyone limit for today is reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The identity door could not be reached; nothing was recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/login":{"post":{"tags":["identity"],"operationId":"login","summary":"Exchange a Cubicle key for a 12-hour deck session.","description":"The key is verified at the identity door and never stored; only the door's verdict is trusted. Success sets the httpOnly deck_session cookie (12 hours); its value is also what the MCP endpoints accept as the bearer. Throttle: 10 refused keys in 5 minutes from one client address rests the door for the remainder of the window; a success clears it.","security":[],"requestBody":{"required":true,"description":"The tfn_live_ key.","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","description":"the principal's tfn_live_ key"}},"required":["key"]}}}},"responses":{"200":{"description":"The session is set in the deck_session cookie; the body names the principal.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"name":{"type":"string"},"agent_id":{"type":"string"}}}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The door refused this key: unknown, pending, rotated or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Not a key (over 512 characters).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many refused keys from this address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/logout":{"post":{"tags":["identity"],"operationId":"logout","summary":"Clear the deck session cookie.","description":"Clears the deck_session cookie. Nothing is revoked at the door; the key stays valid.","security":[],"responses":{"200":{"description":"The cookie is cleared.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/auth/providers":{"get":{"tags":["identity"],"operationId":"authProviders","summary":"Which sign-in doors this deck has open.","description":"Whether the email magic link and X sign-in are configured; the key door is always open. No secrets.","security":[],"responses":{"200":{"description":"{ ok, email, x, key }","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"email":{"type":"boolean"},"x":{"type":"boolean"},"key":{"const":true}}}}}}}}},"/me/key":{"post":{"tags":["identity"],"operationId":"rotateKey","summary":"Issue a fresh key for the signed-in principal; the previous key stops working at that instant.","description":"For a person who signed in by email or X and now wires n8n, an MCP client or a script. The door rotates: the new key is returned once and the deck never stores or logs it. The body must carry {confirm: \"rotate\"} so a stray request cannot break a working integration.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The confirmation word.","content":{"application/json":{"schema":{"type":"object","properties":{"confirm":{"type":"string","description":"must be the word rotate","enum":["rotate"]}},"required":["confirm"]}}}},"responses":{"200":{"description":"The new key, shown once.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"api_key":{"type":"string"},"shown_once":{"type":"boolean"}}}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The confirmation word is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The door refused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The identity door could not be reached; nothing was recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/plans":{"get":{"tags":["plans"],"operationId":"plans","summary":"The plan catalogue, the chains and the treasury; with a session, this principal's own plan too.","description":"The catalogue is public: a prospect sees the price, the chains and the treasury before holding a key. `mine` (plan, usage, period) is returned only with a session and is null without one.","security":[{},{"cubicleKey":[]},{"deckSession":[]}],"responses":{"200":{"description":"The catalogue; `mine` and `principal` are null for an anonymous caller.","content":{"application/json":{"schema":{"type":"object"}}}}}},"post":{"tags":["plans"],"operationId":"claimPlan","summary":"Claim a plan from a transfer already sent on chain.","description":"The door verifies the transfer on chain and records the claim; the deck never reads the chain and never moves money. The door refuses everything until its treasury is configured.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The chain id, the transaction hash and, optionally, the payer's signature.","content":{"application/json":{"schema":{"type":"object","properties":{"chain":{"type":"string","description":"one of the listed chain ids"},"tx_hash":{"type":"string","description":"0x-prefixed 64-hex transaction hash"},"payer_signature_hex":{"type":"string","description":"optional 65-byte 0x signature"}},"required":["chain","tx_hash"]}}}},"responses":{"200":{"description":"The claim as the door recorded it.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The chain, hash or signature failed its shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The door refused; the body carries its answer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The identity door could not be reached; nothing was recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/plans/holder":{"get":{"tags":["plans"],"operationId":"holderState","summary":"The Cubicle Operators holder state the door knows for this principal.","description":"Wallet, NFTs held today, today's allowance, used and remaining.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The holder state.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["plans"],"operationId":"linkHolder","summary":"Link a Cubicle Operators wallet to this principal.","description":"The browser signed HOLDER_MESSAGE(agent_id, day) with the wallet; the door recovers the signer and reads the collection balance itself. The deck stores nothing.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The wallet, the day and the signature.","content":{"application/json":{"schema":{"type":"object","properties":{"wallet_hex":{"type":"string"},"day":{"type":"string"},"signature_hex":{"type":"string"}},"required":["wallet_hex","day","signature_hex"]}}}},"responses":{"200":{"description":"The link as the door recorded it.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The wallet, day or signature failed its shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The door refused; the body carries its answer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The identity door could not be reached; nothing was recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/mandates":{"post":{"tags":["plans"],"operationId":"recordMandate","summary":"Record a Robinhood spot mandate you just granted, so the house operator knows its market.","description":"The market and the EIP-712 struct hash are rebuilt from the signed message; only the house's spot administrators on Robinhood Chain are recorded, and the operator trades only a vault whose on-chain mandateHash matches. Moves no money.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The mandate's typed data, exactly as signed.","content":{"application/json":{"schema":{"type":"object","properties":{"typed_data":{"type":"object"}},"required":["typed_data"]}}}},"responses":{"200":{"description":"The record as stored.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Too large.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Not a mandate the house operator runs; the body says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/grants":{"get":{"tags":["plans"],"operationId":"grants","summary":"The caller's own grant and what it has spent today.","description":"Every principal may read their own budget. `?all=1` returns every key's grant and spend and is house only. Read-only by design: granting is an operator act on a file under git, never an HTTP call.","security":[{"deckSession":[]}],"parameters":[{"name":"all","in":"query","required":false,"description":"1 for every key's grant (house only)","schema":{"type":"string"}}],"responses":{"200":{"description":"The grant and today's spend.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/meter":{"get":{"tags":["plans"],"operationId":"meter","summary":"The caller's balance, allowance, tone and the price list.","description":"The meter reports; it never turns someone away mid-session. Per principal, per day, persisted.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The balance and the price list.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["plans"],"operationId":"meterAccrue","summary":"Charge the caller for one action and return the new balance.","description":"Never refuses: the model lane keeps its own hard budget because that one spends real GPU.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The action and, for bar-priced actions, the bar count.","content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string"},"bars":{"type":"integer"}},"required":["action"]}}}},"responses":{"200":{"description":"The new balance.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/cards":{"get":{"tags":["mcp"],"operationId":"cards","summary":"Every Cubicle seller card on the coordination layer.","description":"One seller per service, each with its own MCP endpoint, record and assurance pool. A card lists only tools that exist and deliver today; NFT market-making is listed when its engine trades.","security":[],"responses":{"200":{"description":"The cards.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/cards/{seller}":{"get":{"tags":["mcp"],"operationId":"card","summary":"One seller's card: the ERC-8004 agentURI and the coordination layer's card in one document.","description":"The same URL answers both readers. `erc8004` carries the identity minted on Base; `classes` stays empty until the layer's judge carries a deterministic check for each proposed class.","security":[],"parameters":[{"name":"seller","in":"path","required":true,"description":"the seller id","schema":{"type":"string","enum":["studio","candles","copy"]}}],"responses":{"200":{"description":"The card.","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"description":"No such seller; the body lists the ids.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/mcp":{"get":{"tags":["mcp"],"operationId":"mcpInfo","summary":"The Cubicle MCP server's name, transport, auth rule and tool names.","description":"A plain description for a person; the protocol is POST.","security":[],"responses":{"200":{"description":"Name, transport, auth and the tool names.","content":{"application/json":{"schema":{"type":"object"}}}}}},"post":{"tags":["mcp"],"operationId":"mcp","summary":"The Cubicle MCP server with every tool (Streamable HTTP, stateless, tools only).","description":"Every tool acts as the signed-in principal: the bearer is forwarded to the deck's own routes, so funding, sealing, logging and refusals are theirs. JSON-RPC 2.0 over Streamable HTTP, stateless, tools only: one POST, one application/json answer, batches allowed, a notification (no id) answers 202. Methods that answer without a key: initialize, ping, tools/list, notifications/initialized, prompts/list, resources/list, resources/templates/list. Tools that answer without a key: catalogue, plans, strategies, strategy_quote, price. Every other tools/call needs the bearer (the cubicleKey scheme) or the deck_session cookie; signed in, tools/list shows what the caller's tier opens and a locked tool answers an upgrade_required result naming the tier that opens it. A keyless call of a gated tool answers resultType input_required with one elicitation/create in URL mode to https://cubicle.algotrada.com/deck/join when the client declared URL-mode elicitation in _meta (MCP 2026-07-28), or JSON-RPC error -32001 whose error.data.next names the same step for an older client. A browser Origin outside the allowlist is refused with 403. An unknown tool is -32602, an unknown method -32601, a bearer that does not verify is -32001 with HTTP 401.","security":[{},{"cubicleKey":[]},{"deckSession":[]}],"requestBody":{"required":true,"description":"One JSON-RPC request, or a batch.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/JsonRpcRequest"},{"type":"array","items":{"$ref":"#/components/schemas/JsonRpcRequest"}}]}}}},"responses":{"200":{"description":"One JSON-RPC response, or a batch of them.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/JsonRpcResponse"},{"type":"array","items":{"$ref":"#/components/schemas/JsonRpcResponse"}}]}}}},"202":{"description":"A notification was received; nothing to answer.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/JsonRpcResponse"},{"type":"array","items":{"$ref":"#/components/schemas/JsonRpcResponse"}}]}}}},"400":{"description":"The body did not parse (JSON-RPC -32700).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The bearer did not verify (JSON-RPC -32001).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The browser Origin is not on the allowlist (JSON-RPC -32600).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/mcp/{seller}":{"get":{"tags":["mcp"],"operationId":"sellerMcpInfo","summary":"One seller's MCP server description: its name and the tools it lists.","description":"The endpoint the seller's coordination-layer card names.","security":[],"parameters":[{"name":"seller","in":"path","required":true,"description":"the seller id","schema":{"type":"string","enum":["studio","candles","copy"]}}],"responses":{"200":{"description":"Name, transport, auth and the tool names.","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"description":"No such seller; the body lists the ids.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["mcp"],"operationId":"sellerMcp","summary":"One Cubicle seller's MCP endpoint: only that seller's tools (and the rail tools) are listed and served.","description":"The same server as POST /mcp, narrowed to the tools the seller's card names plus catalogue, plans and account. JSON-RPC 2.0 over Streamable HTTP, stateless, tools only: one POST, one application/json answer, batches allowed, a notification (no id) answers 202. Methods that answer without a key: initialize, ping, tools/list, notifications/initialized, prompts/list, resources/list, resources/templates/list. Tools that answer without a key: catalogue, plans, strategies, strategy_quote, price. Every other tools/call needs the bearer (the cubicleKey scheme) or the deck_session cookie; signed in, tools/list shows what the caller's tier opens and a locked tool answers an upgrade_required result naming the tier that opens it. A keyless call of a gated tool answers resultType input_required with one elicitation/create in URL mode to https://cubicle.algotrada.com/deck/join when the client declared URL-mode elicitation in _meta (MCP 2026-07-28), or JSON-RPC error -32001 whose error.data.next names the same step for an older client. A browser Origin outside the allowlist is refused with 403. An unknown tool is -32602, an unknown method -32601, a bearer that does not verify is -32001 with HTTP 401.","security":[{},{"cubicleKey":[]},{"deckSession":[]}],"parameters":[{"name":"seller","in":"path","required":true,"description":"the seller id","schema":{"type":"string","enum":["studio","candles","copy"]}}],"requestBody":{"required":true,"description":"One JSON-RPC request, or a batch.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/JsonRpcRequest"},{"type":"array","items":{"$ref":"#/components/schemas/JsonRpcRequest"}}]}}}},"responses":{"200":{"description":"One JSON-RPC response, or a batch of them.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/JsonRpcResponse"},{"type":"array","items":{"$ref":"#/components/schemas/JsonRpcResponse"}}]}}}},"202":{"description":"A notification was received; nothing to answer.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/JsonRpcResponse"},{"type":"array","items":{"$ref":"#/components/schemas/JsonRpcResponse"}}]}}}},"400":{"description":"The body did not parse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"The bearer did not verify.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The browser Origin is not on the allowlist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such seller; the body lists the ids.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/studio/state":{"get":{"tags":["studio"],"operationId":"studioState","summary":"The studio's rigid state machine for this principal's strategy: record, view and coverage.","description":"Eight states; nothing is skipped and no model moves a state.","security":[{"deckSession":[]}],"responses":{"200":{"description":"{ record, view, coverage }","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["studio"],"operationId":"studioAct","summary":"Make one studio move.","description":"`words`, `image` and `reset` change what the rule says and send every later gate back to be re-earned; every other action is accepted only when it is the current state's move (view.next or view.alt). Each action calls the deck's own route as the principal.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The action and its fields.","content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string"}},"required":["action"]}}}},"responses":{"200":{"description":"The state after the move.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Not the current state's move; the body carries the view with the move the studio is waiting for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"The words are over the cap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"An action the machine does not have, or a field that failed its shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The deck's own route behind the move did not answer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/studio/forge":{"get":{"tags":["studio"],"operationId":"studioForge","summary":"One forge this principal cast in the last two hours, as the forge returned it.","description":"Keyed by principal: another person's seed is a 404.","security":[{"deckSession":[]}],"parameters":[{"name":"seed","in":"query","required":true,"description":"the seed hash the forge returned","schema":{"type":"string"}}],"responses":{"200":{"description":"Code, review and scan.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such forge of yours in the window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/studio/rehearse":{"post":{"tags":["studio"],"operationId":"studioRehearse","summary":"Walk this principal's forged machine over the real tape with the canonical stepper.","description":"The forge's own script, kept server-side per principal, is walked bar by bar; the typed envelope (trades, R, the random null it was compared with) is returned whole with a summary. Evidence about the past, never a forecast.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The seed hash; optionally the market and the hours to walk.","content":{"application/json":{"schema":{"type":"object","properties":{"seed_hash":{"type":"string"},"market":{"type":"string","description":"defaults to the market the rule named","enum":["NQ","ES","YM","BTC","ETH","SOL"]},"hours":{"type":"integer"}},"required":["seed_hash"]}}}},"responses":{"200":{"description":"The rehearsal envelope and its summary.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such forge of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A market the tape does not serve, or a bad window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The trial stepper did not answer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/studio/grade":{"post":{"tags":["studio"],"operationId":"studioGrade","summary":"Grade the forge this principal cast with Jev (RUBRIC_v1): code checks the facts, then Jev answers whether the code does what the trader said.","description":"A failed fact check is a reject and Jev is never asked; otherwise one Jev decision is spent, funded the one way every Jev door is (house, own key, plan or trial pings). The grade judges fidelity to the trader's words, never profitability. Nothing is published: only digests leave the deck.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The seed hash.","content":{"application/json":{"schema":{"type":"object","properties":{"seed_hash":{"type":"string"}},"required":["seed_hash"]}}}},"responses":{"200":{"description":"The receipt: every distribution, the reasons, the receipt hash and the decision digest.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"No Jev funding for this principal (unlicensed, or the trial pings are spent); the body names what would fund it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such forge of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The caller's own TypeSafe key is not shaped like one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The Jev lane is unavailable; nothing was spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/studio/score":{"get":{"tags":["studio"],"operationId":"studioCalibration","summary":"The calibration of one stage's Jev score: when Jev said yes here, how often the forge later graded complete.","description":"k/n with a Wilson lower bound, house-wide and this principal's.","security":[{"deckSession":[]}],"parameters":[{"name":"stage","in":"query","required":true,"description":"the stage","schema":{"type":"string","enum":["parts"]}}],"responses":{"200":{"description":"The calibration.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["studio"],"operationId":"studioScore","summary":"Have Jev score one stage with that stage's rubric.","description":"Facts first (a failed fact decides without asking), then the closed questions, then code composes. Recorded so calibration can learn; the trader's words leave the deck only to Jev.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"{ stage: \"parts\", text } or { stage: \"rehearsed\", seed_hash }.","content":{"application/json":{"schema":{"type":"object","properties":{"stage":{"type":"string","enum":["parts","rehearsed"]},"text":{"type":"string"},"seed_hash":{"type":"string"}},"required":["stage"]}}}},"responses":{"200":{"description":"The score and its reasons.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON, or the stage's field is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"No Jev funding for this principal; the body names what would fund it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The rehearsed stage reads the rehearsal: rehearse this forge first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The Jev lane is unavailable; nothing was spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/studio/image":{"post":{"tags":["studio"],"operationId":"studioImage","summary":"Attach a chart image for the studio agent to look at.","description":"The daily image ceiling (free 3, plan 40, house 200) is asked before anything is stored; the type is sniffed from the bytes. The agent sees it through its image tool, as this principal only.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The image, base64.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"string","description":"base64 image bytes"}},"required":["data"]}}}},"responses":{"200":{"description":"{ ok, id, tier, left_today }","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"The image is over the size cap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Not a PNG, JPEG, WebP or GIF.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"No image was sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Today's image ceiling is reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/forge":{"post":{"tags":["forge"],"operationId":"forge","summary":"Compile a rule in words into a portable, watermarked cubicle machine with its review and scan.","description":"Prose becomes a suggested gated state machine (unless the caller brings its own script block), then a decider IR, then watermarked code, the 9-class self-review and the anti-hallucination scan. The engine computes; the route shuttles JSON.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The intent in the trader's words; optionally an author and a script block.","content":{"application/json":{"schema":{"type":"object","properties":{"intent":{"type":"string"},"author":{"type":"string"},"script":{"type":"object"}},"required":["intent"]}}}},"responses":{"200":{"description":"{ ok, seed_hash, version, code, watermark, script, review, scan }","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"The payload is over 256 KB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The intent or the script failed its shape; the error names which.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"A cast is already running; the forge casts a bounded number at a time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The engine failed; the error is carried, never hidden.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["forge"],"operationId":"forgeOnboard","summary":"A prepared onboarding payload for a named algorithm: its spoken intent and its exact script.","description":"Read from the onboarded register; the house's own strategies are sealed.","security":[{"deckSession":[]}],"parameters":[{"name":"onboard","in":"query","required":true,"description":"the algorithm's name","schema":{"type":"string"}}],"responses":{"200":{"description":"The intent and the script.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such onboarded algorithm.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/forge/trial":{"post":{"tags":["forge"],"operationId":"forgeTrial","summary":"Walk the caller's own declared script over the venue tape with the canonical Rust stepper.","description":"The trial's envelope is the answer, ok or a typed refusal; the route computes nothing. NQ, ES and YM default to the cash session because the tape halts at the weekend and breaks daily around 21:00 UTC.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The script and the window.","content":{"application/json":{"schema":{"type":"object","properties":{"script":{"type":"object"},"market":{"type":"string","description":"defaults to NQ","enum":["NQ","ES","YM","BTC","ETH","SOL"]},"hours":{"type":"integer"},"name":{"type":"string"},"tenant":{"type":"string"},"session":{"type":"string"}},"required":["script"]}}}},"responses":{"200":{"description":"The trial envelope, ok or a typed refusal.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"The script is over 64 KiB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"No script, or a market the tape does not serve.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"The deck runs two trials at a time and both are running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The stepper failed; the error is carried.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/forge/lanes":{"get":{"tags":["forge"],"operationId":"forgeLanes","summary":"Which readers this session may use, decided on the server.","description":"An unavailable lane is returned with its reason rather than omitted.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The lanes.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/timetravel/candles/{market}/{tf}":{"get":{"tags":["tape"],"operationId":"candles","summary":"The time machine's candle wire, pre-cached per asset.","description":"The venue keeps about seven days of tape; the deck keeps what it has once seen. The answer carries {coverage}, what was actually measured, so a client trims its window instead of drawing a sparse axis; absent history reads as not measured, never as zeros. Session-scoped requests (a sealed replay) bypass every cache. The same tape is open without a key at https://clob.taifoon.dev/tape/v1/candles/{market}/{tf} (x-related.openTape).","security":[{"deckSession":[]}],"parameters":[{"name":"market","in":"path","required":true,"description":"the market","schema":{"type":"string","enum":["NQ","ES","YM","BTC","ETH","SOL"]}},{"name":"tf","in":"path","required":true,"description":"the timeframe","schema":{"type":"string","enum":["15s","1m","5m","1h"]}},{"name":"from","in":"query","required":false,"description":"window start, unix seconds","schema":{"type":"integer"}},{"name":"to","in":"query","required":false,"description":"window end, unix seconds","schema":{"type":"integer"}},{"name":"session","in":"query","required":false,"description":"a replay session id; bypasses every cache","schema":{"type":"string"}}],"responses":{"200":{"description":"The candles and their coverage.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A resolution, market or window the tape refuses.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The venue did not answer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/forecast/{market}/{tf}":{"get":{"tags":["tape"],"operationId":"forecastEnvelope","summary":"The envelope the forecast sidecar had issued by a logical time: magnitude, never direction.","description":"An envelope.v1 (lib/envelope): a band symmetric about the last close over the next 24 bars, the odds of a 0.5/1/2 ATR move within 6/12/24 bars, and the measured calibration (ece, n, coverage). There is no side, no median and no drift of the band — the deck refuses any answer that carries one. `draw` and `label` are computed here from the calibration (solid under ece 0.05, dashed under 0.10, otherwise none: not drawn). Only what was issued by `t` is disclosed, so a replay scrubbing back un-happens later forecasts. The sidecar forecasts 5m and 1h. With `history=1&from` it answers the issues in [from, t] instead, compact (issued_t, width in ATR, the band's two ends) — the time compressor's ≋ beats.","security":[{"deckSession":[]}],"parameters":[{"name":"market","in":"path","required":true,"description":"the market","schema":{"type":"string","enum":["NQ","ES","YM","BTC","ETH","SOL"]}},{"name":"tf","in":"path","required":true,"description":"the timeframe the sidecar forecasts","schema":{"type":"string","enum":["5m","1h"]}},{"name":"t","in":"query","required":false,"description":"the logical time the envelope must have been issued by, unix seconds (default: now)","schema":{"type":"integer"}},{"name":"session","in":"query","required":false,"description":"a replay session id; the sidecar reads the tape under that session's seal","schema":{"type":"string"}},{"name":"history","in":"query","required":false,"description":"1 for the issues in [from, t]","schema":{"type":"string","enum":["1"]}},{"name":"from","in":"query","required":false,"description":"with history=1: window start, unix seconds","schema":{"type":"integer"}}],"responses":{"200":{"description":"{ ok, envelope, draw, label, served, age_bars } — or, with history=1, { ok, issues, truncated, draw, label }","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No envelope had been issued by t.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A market or timeframe the sidecar does not forecast.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The sidecar did not answer, or answered with something that is not an envelope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"No sidecar is configured for this deck.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/forecast/health":{"get":{"tags":["tape"],"operationId":"forecastLens","summary":"The envelope lens: per market and timeframe, the measured calibration and the age of the newest issue.","description":"The library's lens card. Each row's label and draw rule are re-derived by the deck from the sidecar's numbers (ece, n, band coverage), so a pair scored on fewer than 30 forecasts reads unmeasured whatever the sidecar says. Read-only; the sidecar stays loopback-only behind it.","security":[{"deckSession":[]}],"responses":{"200":{"description":"{ ok, model, model_rev, device, samples, horizon, rows }","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The sidecar did not answer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"No sidecar is configured for this deck.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/marks":{"get":{"tags":["records"],"operationId":"myMarks","summary":"The caller's own levels and zones, and the sentence the studio takes them as.","description":"A mark is a price the trader placed on the chart by hand — their words, drawn. `words` states the marks for one market as levels the trader gave, which is what the studio may use.","security":[{"deckSession":[]}],"parameters":[{"name":"market","in":"query","required":false,"description":"one market's marks (and its studio sentence)","schema":{"type":"string","enum":["NQ","ES","YM","BTC","ETH","SOL"]}}],"responses":{"200":{"description":"{ ok, marks, words }","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"An unknown market.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["records"],"operationId":"addMark","summary":"Place a level or a zone.","description":"Validated in code before it is stored: a known market, finite positive prices, a zone with two different prices, a short plain note; at most 20 per market and 60 in all.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The mark.","content":{"application/json":{"schema":{"type":"object","properties":{"market":{"type":"string","description":"the market","enum":["NQ","ES","YM","BTC","ETH","SOL"]},"kind":{"type":"string","description":"level or zone","enum":["level","zone"]},"price":{"type":"number"},"price2":{"type":"number"},"note":{"type":"string"}},"required":["market","kind","price"]}}}},"responses":{"200":{"description":"{ ok, mark, marks }","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"No JSON body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The cap is reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The mark is not valid, and why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["records"],"operationId":"removeMark","summary":"Remove one of the caller's marks.","description":"Only the caller's own; another principal's mark id is not found.","security":[{"deckSession":[]}],"parameters":[{"name":"id","in":"query","required":true,"description":"the mark id","schema":{"type":"string"}}],"responses":{"200":{"description":"{ ok, marks }","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such mark of yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Not a mark id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/plates":{"post":{"tags":["tape"],"operationId":"sealPlate","summary":"Seal what the chart shows for a past window into a plate anyone with the link can see.","description":"Rendered by the chart_image tool as the caller: the deck's own plate (bars, the chosen indicators, the envelope only under its draw gate, the caller's marks on that market) plus the chart_state numbers it was drawn from. The id is the hash of both, so the same seal returns the same id. A plate never names who sealed it. 40 seals per principal per UTC day.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The window and the layers.","content":{"application/json":{"schema":{"type":"object","properties":{"market":{"type":"string","description":"the market","enum":["NQ","ES","YM","BTC","ETH","SOL"]},"tf":{"type":"string","description":"timeframe","enum":["15s","1m","5m","1h"]},"from":{"type":"string","description":"window start, ISO-8601 UTC"},"to":{"type":"string","description":"window end, ISO-8601 UTC, in the past"},"indicators":{"type":"array","items":{"type":"string"}}},"required":["market","tf","from","to"]}}}},"responses":{"200":{"description":"{ ok, id, fresh, path, png, json }","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A window, market or indicator the chart tools refuse, or a picture that could not be drawn.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Today's seals are used.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["tape"],"operationId":"myPlates","summary":"The caller's own sealed plates, newest first.","description":"Listed from a per-principal index filed under a hash of the principal; the plates themselves name nobody.","security":[{"deckSession":[]}],"responses":{"200":{"description":"{ ok, plates, per_day, today }","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/plates/{id}":{"get":{"tags":["tape"],"operationId":"readPlate","summary":"A sealed plate's numbers: the chart_state it was drawn from.","description":"Open without a key: the id is the content hash, immutable.","security":[],"parameters":[{"name":"id","in":"path","required":true,"description":"the plate id, 32 hex","schema":{"type":"string"}}],"responses":{"200":{"description":"{ ok, plate }","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"description":"No such plate.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/plates/{id}/png":{"get":{"tags":["tape"],"operationId":"readPlatePng","summary":"A sealed plate's picture.","description":"Open without a key; immutable because the id is its hash.","security":[],"parameters":[{"name":"id","in":"path","required":true,"description":"the plate id, 32 hex","schema":{"type":"string"}}],"responses":{"200":{"description":"The PNG.","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"description":"No such plate.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/timetravel/session":{"post":{"tags":["tape"],"operationId":"replaySession","summary":"Open a sealed replay session on the venue with its own warped clock.","description":"Every session is compressed separately; two clients never share a clock. speed_x is clamped to the rail's own pills and the venue enforces its own limits regardless.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The markets, the window and the speed.","content":{"application/json":{"schema":{"type":"object","properties":{"markets":{"type":"array","items":{"type":"string"}},"from":{"type":"string"},"to":{"type":"string"},"speed_x":{"type":"number"}},"required":["markets","from","to"]}}}},"responses":{"200":{"description":"The session.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Markets, window or speed failed their shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The venue did not answer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["tape"],"operationId":"replayClock","summary":"A replay session's live clock.","description":"speed_x, t_logical, mode wall or replay.","security":[{"deckSession":[]}],"parameters":[{"name":"session","in":"query","required":true,"description":"the session id","schema":{"type":"string"}}],"responses":{"200":{"description":"The clock.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/timetravel/warm":{"get":{"tags":["tape"],"operationId":"warm","summary":"Kick the candle pre-cache and report how much history it holds per market and timeframe.","description":"Idempotent: the first call starts the warmer. Chunk counts and errors only; no tenant data and no candles.","security":[],"responses":{"200":{"description":"The store's coverage per (market, tf) and its error count.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/skills":{"get":{"tags":["skills"],"operationId":"skills","summary":"The Cubicle skill bank, read from the skills' own SKILL.md frontmatter.","description":"Name, family, gate, status (stub, runnable, built; never smoothed away), autonomy, version and scripts per skill, with counts by status and family. Outside the house only the methodology and engineering skills are listed; the rest is a count, because gate names are the strategy. A stub is a promise, not a capability.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The bank; `reachable: false` with the reason when it cannot be read.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/history":{"get":{"tags":["records"],"operationId":"myHistory","summary":"The signed-in principal's own venue account: balance, positions, closed round trips and daily series, broken out per algo.","description":"No attribution parameter and no way to read another principal's book. Each section carries its own verdict; unreachable is not empty.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The account.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/welcome":{"get":{"tags":["records"],"operationId":"welcome","summary":"Whether to show the first-landing section and what it needs to say.","description":"Shown to people who came in through email or X and have not dismissed it.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The section's state.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["records"],"operationId":"dismissWelcome","summary":"Dismiss the first-landing section, persisted.","description":"It does not return on the next visit.","security":[{"deckSession":[]}],"responses":{"200":{"description":"Dismissed.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/roadmap":{"get":{"tags":["records"],"operationId":"roadmap","summary":"The delivery matrix as data, parsed from the one roadmap file.","description":"Every feature row carries the graded tag it earned by a probe (real, building, proposed, blocked, done); the deck never invents a status. If the file is absent the route says so.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The matrix, or a plain statement that the file is absent.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/gate":{"get":{"tags":["records"],"operationId":"gateLadder","summary":"The algo gate ladder's state of record, stamped by the compiler.","description":"Never hand-edited and never computed here; if the file is absent the route says so.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The ladder state, or a plain statement that it is absent.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/algo/registry":{"get":{"tags":["records"],"operationId":"algoRegistry","summary":"The onboarded machines the deck can render and download.","description":"One row per house machine and one per tenant machine; `?download=<name>[&tenant=<t>]` returns the raw file as an attachment. The house's own strategies are sealed.","security":[{"deckSession":[]}],"parameters":[{"name":"download","in":"query","required":false,"description":"a machine's name, to download its file","schema":{"type":"string"}},{"name":"tenant","in":"query","required":false,"description":"the tenant whose machine it is","schema":{"type":"string"}}],"responses":{"200":{"description":"The register, or the file.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The machine's tenant declared its owners and this principal is not among them.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No machine of that name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["records"],"operationId":"algoUpload","summary":"Upload a machine under the caller's own tenant.","description":"The document must satisfy the script validator (named refusals: unknown references, unreachable states, terminal exits, ungated edges, unbounded documents); writes land only under the tenant's own directory and every accepted upload appends to the hash-chained script audit with the session principal as the actor.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The machine.","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"tenant":{"type":"string"},"doc":{"type":"object"}},"required":["name","tenant","doc"]}}}},"responses":{"200":{"description":"Accepted and recorded.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"This tenant declared its owners and this principal is not among them.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"The machine is over 128 KB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The document failed validation; the refusal is named.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"The write failed; the error is carried.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/algo/walk":{"get":{"tags":["records"],"operationId":"algoWalk","summary":"One session of an onboarded algorithm's real state machine, bar by bar.","description":"Every frame is the walker's, with each guard's live value, its threshold, whether it is met and the gap. `upto` truncates the feed, not the output.","security":[{"deckSession":[]}],"parameters":[{"name":"algo","in":"query","required":true,"description":"the algorithm","schema":{"type":"string"}},{"name":"date","in":"query","required":false,"description":"YYYY-MM-DD","schema":{"type":"string"}},{"name":"upto","in":"query","required":false,"description":"bars to feed","schema":{"type":"integer"}},{"name":"list","in":"query","required":false,"description":"1 for the clean session pool","schema":{"type":"string"}}],"responses":{"200":{"description":"The walk, or the session pool.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/book/orders":{"post":{"tags":["book"],"operationId":"placeOrder","summary":"Place an order on the venue in the caller's own namespace, through the server-side exposure guard.","description":"The attribution is derived from the verified session, never from the body: the house keeps the cubicle-deck desk, every other principal trades cubicle-deck:u-<tag>. A foreign attribution is refused, qty has a per-market ceiling, exposure and dedupe are per namespace, and a same-way order on top of an existing position is refused (flattens pass).","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The order, as the venue's /book/v1/orders takes it; the attribution is overwritten with the caller's own.","content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"The venue's answer.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON, a foreign attribution, or a qty over the ceiling.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A same-way clip already stands in this market: no stacking.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"The venue did not answer in time; the order may or may not have rested, read positions before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/book/orders/{id}":{"delete":{"tags":["book"],"operationId":"cancelOrder","summary":"Cancel one of the caller's own orders.","description":"Only the deck's own order and cancel routes may write to the book; every other /book write is refused by the middleware with 405.","security":[{"deckSession":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"the order id","schema":{"type":"string"}}],"responses":{"200":{"description":"The venue's answer.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Not an order id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"The venue did not answer in time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/book/fills":{"get":{"tags":["book"],"operationId":"fills","summary":"The deck's own recent fills, server-thinned from the venue's whole tape.","description":"Fetched once, filtered to the deck's attribution, cached briefly.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The fills.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/book/history":{"get":{"tags":["book"],"operationId":"bookHistory","summary":"One browsable history for any actor on the book, from the venue's own per-actor rollups.","description":"Synthetic book actors (simulated depth and flow bots) are not a benchmark; a record is past fills, never a forecast.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The history.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/nemotron":{"get":{"tags":["book"],"operationId":"nemotron","summary":"The one hosted LLM agent's live record: liveness derived from its last trade, realized and unrealized never summed, the base rate shipped with the record.","description":"The venue is the source of truth for fills, positions and PnL; the deck derives nothing it could get from the venue.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The record with its staleness verdict.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/nemotron/trades":{"get":{"tags":["book"],"operationId":"nemotronTrades","summary":"The agent's round trips, FIFO-paired by the venue.","description":"Entry and exit price and time, realized PnL, win or loss, both fill ids.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The closed trades.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/nemotron/trail":{"get":{"tags":["book"],"operationId":"nemotronTrail","summary":"The agent's own append-only log trail, read-only.","description":"What the agent did, not what an operator did in a tab.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The trail; `reachable: false` with the reason when the trail service did not answer, never an empty list.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/plane/logs":{"get":{"tags":["plane"],"operationId":"planeLogs","summary":"The deck's wire to the observability plane: per-algorithm labelled log streams.","description":"Server-side through the plane-logs skill, so the browser never holds a credential. `cannot see the data` reads differently from `no data`.","security":[{"deckSession":[]}],"parameters":[{"name":"algo","in":"query","required":false,"description":"the algorithm slug","schema":{"type":"string"}},{"name":"lane","in":"query","required":false,"description":"fsm, pods, chartbox or journal","schema":{"type":"string","enum":["fsm","pods","chartbox","journal"]}},{"name":"component","in":"query","required":false,"description":"a CXX component","schema":{"type":"string"}},{"name":"tenant","in":"query","required":false,"description":"the tenant partition","schema":{"type":"string"}},{"name":"level","in":"query","required":false,"description":"INFO, DEBUG, WARN or ERROR","schema":{"type":"string"}},{"name":"since","in":"query","required":false,"description":"a duration such as 15m","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"line cap","schema":{"type":"integer"}}],"responses":{"200":{"description":"Streams, lines and totals, or the skill's typed error verbatim.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/plane/tenants":{"get":{"tags":["plane"],"operationId":"planeTenants","summary":"The tenants the plane has evidence for.","description":"The tenant label's distinct values; values only, never lines.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The tenants.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/cxx/attach":{"post":{"tags":["plane"],"operationId":"attachLogs","summary":"Put the caller's own cockpit lines on the plane, under the caller's own tenant.","description":"The tenant is resolved server-side from the session, never from the body: evidence that can be forged is not evidence.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The lines.","content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Accepted.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/flows":{"get":{"tags":["plane"],"operationId":"flows","summary":"A tenant's recent flow lines (every deliberate operator action on a panel), newest last.","description":"`?map=1` serves the whole panel-to-component pairing.","security":[{"deckSession":[]}],"parameters":[{"name":"tenant","in":"query","required":false,"description":"the tenant slug","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"line cap","schema":{"type":"integer"}},{"name":"map","in":"query","required":false,"description":"1 for the pairing","schema":{"type":"string"}}],"responses":{"200":{"description":"The lines, or the pairing.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"That ledger belongs to another tenant; the body names yours.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Not a tenant slug.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["plane"],"operationId":"recordFlow","summary":"Record one deliberate operator action, validated against closed sets and hash-chained per tenant.","description":"The actor is always the session principal, never client-claimed; a label outside the closed sets is refused, never recorded.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The flow.","content":{"application/json":{"schema":{"type":"object","properties":{"panel":{"type":"string"},"control":{"type":"string"},"action":{"type":"string"},"detail":{"type":"string"},"tenant":{"type":"string"}},"required":["panel","control","action"]}}}},"responses":{"200":{"description":"Recorded.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"That ledger belongs to another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A label outside the closed sets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/glyphs":{"get":{"tags":["plane"],"operationId":"glyphs","summary":"Glyph wiki suggestions, or a tenant's designed glyph set.","description":"`?term=` suggests (deterministic, free); `?tenant=&algo=` returns the merged set; `&download=1` the raw file, which the tenant owns.","security":[{"deckSession":[]}],"parameters":[{"name":"term","in":"query","required":false,"description":"text to suggest glyphs for","schema":{"type":"string"}},{"name":"tenant","in":"query","required":false,"description":"the tenant slug","schema":{"type":"string"}},{"name":"algo","in":"query","required":false,"description":"the algorithm slug","schema":{"type":"string"}},{"name":"download","in":"query","required":false,"description":"1 for the raw file","schema":{"type":"string"}}],"responses":{"200":{"description":"Suggestions, the set, or the file.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"That set belongs to another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A slug or term that failed its shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"tags":["plane"],"operationId":"designGlyph","summary":"Design one mark for a tenant's algorithm, appended to the hash-chained glyph audit.","description":"Validated against the register (a mark, never a word); the actor is the session principal.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The mark.","content":{"application/json":{"schema":{"type":"object","properties":{"tenant":{"type":"string"},"algo":{"type":"string"},"kind":{"type":"string"},"key":{"type":"string"},"glyph":{"type":"string"}},"required":["tenant","algo","kind","key","glyph"]}}}},"responses":{"200":{"description":"Written and audited.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"That set belongs to another tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Not a mark, or a slug that failed its shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/tenancy":{"get":{"tags":["plane"],"operationId":"tenancy","summary":"The history of tenancy declarations, including contested ones.","description":"A claim is not a transfer; the interesting event is a principal reaching for a tenant somebody else already declared.","security":[{"deckSession":[]}],"responses":{"200":{"description":"The declarations.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["plane"],"operationId":"declareTenancy","summary":"Declare a tenancy for this principal.","description":"Recorded with its outcome, contested or not.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The tenant slug.","content":{"application/json":{"schema":{"type":"object","properties":{"tenant":{"type":"string"}},"required":["tenant"]}}}},"responses":{"200":{"description":"Declared.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Another principal already declared this tenant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Not a tenant slug.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The declaration store did not answer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/chat":{"post":{"tags":["chat"],"operationId":"chat","summary":"Ask the deck's chat (Nemotron through a fair queue): returns a ticket at once.","description":"Ticket-and-poll rather than one long request, so a person sees their place in line. A licensed principal chats within its daily token ceiling; everyone else gets a bounded number of free turns a day (20 by default). Every reply passes the chat guard before it is shown.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The messages, ending with the question.","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"role":{"type":"string"},"content":{"type":"string"}}}}},"required":["messages"]}}}},"responses":{"200":{"description":"{ ticket, state, ahead, eta_s }","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"A lane this principal is not licensed for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The messages did not end with a user turn.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Today's free turns or the token ceiling are spent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["chat"],"operationId":"chatPoll","summary":"Poll a chat ticket until the reply is ready.","description":"{ state, ahead, eta_s, reply? }","security":[{"deckSession":[]}],"parameters":[{"name":"t","in":"query","required":true,"description":"the ticket","schema":{"type":"string"}}],"responses":{"200":{"description":"The ticket's state and, when done, the reply.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such ticket.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/agent/run":{"post":{"tags":["agent"],"operationId":"agentRun","summary":"Mint a venue replay session and point the venue's driver at the operator's own decide() endpoint.","description":"The venue caps sessions at 5 per IP, so the deck reaps the session it minted before minting a new one; it only ever deletes its own.","security":[{"deckSession":[]}],"requestBody":{"required":true,"description":"The decide() URL and the window.","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"the operator's decide() endpoint"},"market":{"type":"string","description":"defaults to BTC-PERP"},"tf":{"type":"string","description":"defaults to 1m"},"speedX":{"type":"number","description":"1 to 10000, default 100"},"minutes":{"type":"integer","description":"5 to 1440, default 120"}},"required":["url"]}}}},"responses":{"200":{"description":"The run.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"The body was not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Not a decide() URL, or a window outside the bounds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"The venue's session cap for this address is reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The venue refused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"The venue did not answer in time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["agent"],"operationId":"agentStop","summary":"Stop the deck's run and delete the venue session it minted.","description":"Only the session the deck minted is deleted.","security":[{"deckSession":[]}],"responses":{"200":{"description":"Stopped.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/agent/status":{"get":{"tags":["agent"],"operationId":"agentStatus","summary":"The run's live counters and its receipt, in one read.","description":"The receipt 404s at the venue until a driver has run, which is a legitimate state (not started), not an error.","security":[{"deckSession":[]}],"responses":{"200":{"description":"{ status, receipt }","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"No session to read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Not a session id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"The venue did not answer in time.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/lab/jev":{"get":{"tags":["records"],"operationId":"labJev","summary":"The jev-hft-mirror study as data: every report and every forged bundle, re-runnable with the public candle API.","description":"The study is concluded: six pre-registered trials found no edge. The route reads; it never computes a number and never runs a trial.","security":[{"deckSession":[]}],"parameters":[{"name":"view","in":"query","required":false,"description":"page for the rendered study","schema":{"type":"string"}},{"name":"report","in":"query","required":false,"description":"one report's name","schema":{"type":"string"}},{"name":"bundle","in":"query","required":false,"description":"a forged bundle","schema":{"type":"string"}},{"name":"file","in":"query","required":false,"description":"one file of that bundle","schema":{"type":"string"}}],"responses":{"200":{"description":"The index, a report, the page or a file.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"No deck session: sign in with POST /login first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/typed/openapi":{"get":{"tags":["typed"],"operationId":"typedOpenApi","summary":"The typed-decision lane's own OpenAPI 3.1 document (JSON).","description":"Ask, translate and chat over TypeSafe's Jev with the caller's own key; the lane is documented there, not here. Public: a contract holds no secret.","security":[],"responses":{"200":{"description":"The lane's OpenAPI document.","content":{"application/json":{"schema":{"type":"object"}}}}}}}},"components":{"securitySchemes":{"cubicleKey":{"type":"http","scheme":"bearer","bearerFormat":"deck session","description":"A Cubicle session for one principal, sent as Authorization: Bearer <token> to the MCP endpoints. The token is the value POST /login sets as the deck_session cookie (12 hours) after verifying a tfn_live_ key at the identity door; the studio agent runs on a 10-minute one. The MCP server verifies the bearer with the same verifier as the cookie, so a raw tfn_live_ key sent as the bearer is refused (-32001, HTTP 401): exchange it at POST /login first. Mint a key at https://cubicle.algotrada.com/deck/join, once, as the person."},"deckSession":{"type":"apiKey","in":"cookie","name":"deck_session","description":"The httpOnly session cookie POST /login sets (12 hours). Every route not listed as anonymous needs it; the middleware answers 401 without it."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"ok":{"const":false},"error":{"type":"string"},"detail":{"type":"string"},"retryable":{"type":"boolean"}}},"JoinRequest":{"type":"object","properties":{"name":{"type":"string","description":"3 to 32 characters of a-z, 0-9, - and _; lower-cased; the house's names are reserved","pattern":"^[a-z0-9_-]{3,32}$"},"wallet":{"type":"string","description":"optional 0x address; it becomes the principal's operator","pattern":"^0x[0-9a-fA-F]{40}$"}},"required":["name"]},"JoinResponse":{"type":"object","properties":{"ok":{"const":true},"name":{"type":"string"},"agent_id":{"type":"string"},"api_key":{"type":"string","description":"the tfn_live_ key, shown once; it cannot be shown again, only rotated"},"shown_once":{"const":true},"next":{"type":"string"}},"required":["ok","name","agent_id","api_key"]},"JsonRpcRequest":{"type":"object","properties":{"jsonrpc":{"const":"2.0"},"id":{"type":["string","integer","null"],"description":"absent for a notification, which is not answered"},"method":{"type":"string","description":"initialize, ping, tools/list, prompts/list, resources/list, resources/templates/list or tools/call"},"params":{"$ref":"#/components/schemas/McpParams"}},"required":["jsonrpc","method"]},"McpParams":{"type":"object","properties":{"name":{"type":"string","description":"tools/call: the tool"},"arguments":{"type":"object","description":"tools/call: the tool's input, by its inputSchema"},"requestState":{"type":"string","description":"the opaque state an earlier input_required answer carried; echo it on the retry"},"_meta":{"type":"object","description":"a 2026-07-28 client's io.modelcontextprotocol/protocolVersion, clientInfo and clientCapabilities; absent means an older client"}}},"JsonRpcResponse":{"type":"object","properties":{"jsonrpc":{"const":"2.0"},"id":{"type":["string","integer","null"]},"result":{"description":"the method's result; a tools/call result is the tool's content, or an InputRequired for a gated tool called without a key by a URL-capable client"},"error":{"$ref":"#/components/schemas/JsonRpcError"}},"required":["jsonrpc","id"]},"JsonRpcError":{"type":"object","properties":{"code":{"type":"integer","description":"-32700 parse, -32600 origin refused, -32601 method unknown, -32602 tool unknown, -32001 unauthenticated"},"message":{"type":"string"},"data":{"type":"object","properties":{"next":{"$ref":"#/components/schemas/KeyStep"}}}},"required":["code","message"]},"KeyStep":{"type":"object","properties":{"step":{"const":"key"},"who":{"const":"person"},"why":{"type":"string"},"how":{"type":"object","properties":{"kind":{"const":"url"},"url":{"type":"string","description":"https://cubicle.algotrada.com/deck/join?from=mcp&tool=<tool>"},"note":{"type":"string"}}},"api":{"type":"object","properties":{"method":{"const":"POST"},"path":{"const":"/deck/api/join"},"body":{"type":"object"}}}}},"InputRequired":{"type":"object","properties":{"resultType":{"const":"input_required"},"inputRequests":{"type":"object","properties":{"cubicle_key":{"type":"object","properties":{"method":{"const":"elicitation/create"},"params":{"type":"object","properties":{"mode":{"const":"url"},"url":{"type":"string"},"message":{"type":"string"}}}}}}},"requestState":{"type":"string","description":"HMAC-protected, bound to the tool and an issue time, valid for an hour; tampering can cause nothing worse than the same answer again"},"_meta":{"type":"object"}},"required":["resultType","inputRequests","requestState"]}}},"x-mcp":{"transport":"streamable-http, stateless, tools only","initializeProtocolVersion":"2025-06-18","recognisedClientProtocolVersion":"2026-07-28","anonymousMethods":["initialize","ping","tools/list","notifications/initialized","prompts/list","resources/list","resources/templates/list"],"anonymousTools":["catalogue","plans","strategies","strategy_quote","price"],"allowedBrowserOrigins":["https://cubicle.algotrada.com","http://localhost:3077","http://localhost:3078","http://localhost:3079","http://127.0.0.1:3077","http://127.0.0.1:3078","http://127.0.0.1:3079"],"keyless":{"rule":"a gated tool called without a key is never served; the person is sent to the join page out of band and the server never mints the key","url":"https://cubicle.algotrada.com/deck/join","urlElicitationFor":"2026-07-28","olderClients":"JSON-RPC error -32001 with error.data.next (schema KeyStep)"},"servers":[{"name":"cubicle","url":"https://cubicle.algotrada.com/deck/api/mcp","tools":["catalogue","account","plans","plan_claim","models","desk_read","model_record","follow_quote","strategies","price","strategy_quote","my_vaults","vault_prepare","workflow_read","studio_move","forge","grade","rehearse","tape","forecast","chart_state","chart_image","image","skills","skill_read"],"note":"every tool; the studio agent's own server and a principal's own client"},{"name":"cubicle-studio","url":"https://cubicle.algotrada.com/deck/api/mcp/studio","card":"https://cubicle.algotrada.com/deck/api/cards/studio","tools":["workflow_read","studio_move","forge","rehearse","skills","skill_read","catalogue","plans","plan_claim","account"],"wallet":"0x3186102418b8F127C7a4BE803a77D5b767E60ef4","erc8004AgentId":96558,"proposedClasses":["strategy.rehearse"]},{"name":"cubicle-candles","url":"https://cubicle.algotrada.com/deck/api/mcp/candles","card":"https://cubicle.algotrada.com/deck/api/cards/candles","tools":["price","tape","forecast","chart_state","chart_image","catalogue","plans","plan_claim","account"],"wallet":"0xddf57bfebF4AE61aA7529A8B8155A383Fed9Ede1","erc8004AgentId":96559,"proposedClasses":["market.candles"]},{"name":"cubicle-copy","url":"https://cubicle.algotrada.com/deck/api/mcp/copy","card":"https://cubicle.algotrada.com/deck/api/cards/copy","tools":["models","model_record","follow_quote","strategies","strategy_quote","my_vaults","vault_prepare","catalogue","plans","plan_claim","account"],"wallet":"0x97FB114d3F5d700B7837f35B02c6ADb07A4fb991","erc8004AgentId":96560,"proposedClasses":["copy.follow_quote"]}],"tools":[{"name":"catalogue","description":"The whole Cubicle API as data: the studio's eight-state machine and what each gate asks, every tool with the tier that opens it (and whether it is open to the caller), and the flows. Call it first when asked what Cubicle can do.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[],"sellers":["studio","candles","copy"]},{"name":"account","description":"The caller's Cubicle account: their tier (free, Cubicle Operators holder, pro-50, max premium-99), their plan and until when, and every capability with whether it is open and which tier opens it. Call it before telling a trader what they can or cannot do.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["studio","candles","copy"]},{"name":"plans","description":"The Cubicle plans (price per 30 days, what each includes) and exactly how to pay on Robinhood Chain in USDG: the treasury, the token, and — when the subscription vault is live — the two wallet transactions (approve, subscribe) that renew automatically every 30 days. Nothing here moves money: the trader signs in their own wallet.","inputSchema":{"type":"object","properties":{"plan":{"type":"string","description":"a plan id (pro-50 or premium-99) to prepare the payment steps for","enum":["pro-50","premium-99"]},"wallet":{"type":"string","description":"the 0x address of the wallet that will pay — needed for the renewing subscription, which is authorized for that wallet only"}},"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[],"sellers":["studio","candles","copy"]},{"name":"plan_claim","description":"Claim a plan you paid for on chain: give the payment's transaction hash and the paying wallet's EIP-191 signature over the exact message the plans tool returns (\"Cubicle plan claim\\nagent: <your agent id>\\ntx: <tx hash>\"). The door reads the transfer to the treasury itself and opens the plan for this principal; only the wallet that paid can claim it. Needs your Cubicle key.","inputSchema":{"type":"object","properties":{"tx_hash":{"type":"string","description":"the payment transaction's hash (0x + 64 hex)"},"payer_signature_hex":{"type":"string","description":"the paying wallet's EIP-191 signature (0x + 130 hex) over the claim message"},"chain":{"type":"string","description":"the chain the payment was made on (Cubicle takes plans on Robinhood Chain only)","enum":["robinhood"]}},"required":["tx_hash","payer_signature_hex"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true},"security":[{"cubicleKey":[]}],"sellers":["studio","candles","copy"]},{"name":"models","description":"The trading LLM models on the Taifoon venue a trader can study and, on the max plan, follow: each with its status (trading or stopped, derived from its last trade), markets, fills, realized PnL and win rate — all read from the venue, never from a model's words. Simulated market-maker and flow actors are never listed.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["copy"]},{"name":"desk_read","description":"The house Nemotron desk (llm-nemotron-fest) as it is: its last turn (every 30 minutes), each market's decision and which reader decided it (the vote rule the Nemotron answers, or Jev), the facts it read, and the venue's own positions with realised and unrealised PnL. Composed from the desk's log and the venue, no model asked; a turn older than 45 minutes is marked stale. Not a forecast.","inputSchema":{"type":"object","properties":{"market":{"type":"string","description":"one market, or every market when absent","enum":["BTC","ETH","SOL"]}},"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":[]},{"name":"model_record","description":"One trading model's record from the venue's first-in-first-out paired closes: realized PnL, maximum drawdown, win rate, per market, and the window. Always say what the record is not: a forecast, or what a follower would have made.","inputSchema":{"type":"object","properties":{"model":{"type":"string","description":"the model's attribution, from the models tool (starts with llm-)"}},"required":["model"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["copy"]},{"name":"follow_quote","description":"Quote following a trading model (max plan): checks the model is live and followable, validates the follower's sizing (fixed_notional usd, or proportional bps of their balance) and caps (max_notional_usd, max_open_positions, max_daily_loss_usd, markets the model trades), and shows what one mirror would be at the given balance. Creates nothing and moves nothing.","inputSchema":{"type":"object","properties":{"model":{"type":"string","description":"the model's attribution from the models tool"},"sizing":{"type":"object","description":"{\"kind\":\"fixed_notional\",\"usd\":50} or {\"kind\":\"proportional\",\"bps\":200}"},"caps":{"type":"object","description":"{\"max_notional_usd\":100,\"max_open_positions\":3,\"max_daily_loss_usd\":40,\"markets\":[\"BTC\"]}"},"balance_usd":{"type":"number","description":"the follower's balance, to show a mirror's size (optional)"}},"required":["model","sizing","caps"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["copy"]},{"name":"strategies","description":"The strategies anyone can rent to run a Cubicle spot vault (today: accumulate, which buys a fixed amount of ETH or BTC on an interval up to a budget and never sells): what each does, its parameters, the venues and assets, the limits the vault itself enforces, the fee, how to rent it, and its honest status (not live with real funds yet).","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[],"sellers":["copy"]},{"name":"price","description":"FREE, no key, no sign-in: the tape's latest price for one market (NQ, ES, YM, BTC, ETH or SOL) with the time of that 1-minute close and whether the tape is stale. Use it whenever an agent needs a current price; it is the same tape every Cubicle vault checks its orders against. For a window of candles use tape (needs a key).","inputSchema":{"type":"object","properties":{"symbol":{"type":"string","description":"the market, by ticker","enum":["NQ","ES","YM","BTC","ETH","SOL"]}},"required":["symbol"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true},"security":[],"sellers":["candles"]},{"name":"strategy_quote","description":"A live quote for one accumulate buy on Robinhood Chain: what a USD amount of USDG buys of ETH or BTC on the best Uniswap route right now, the tape's price, the cost against the tape in bps, and whether the vault's own price rule (at most 1% worse than the tape) would accept it. A static call: nothing is signed or sent.","inputSchema":{"type":"object","properties":{"asset":{"type":"string","description":"the asset","enum":["ETH","BTC"]},"usd":{"type":"number","description":"USD of USDG to spend"}},"required":["asset","usd"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true},"security":[],"sellers":["copy"]},{"name":"my_vaults","description":"The caller's trading vaults, read from the chain: each with whether its funds are devnet (test money) or mainnet (real), what is in it, what is free, what is set aside as margin, the mandate and who administers it.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true},"security":[{"cubicleKey":[]}],"sellers":["copy"]},{"name":"vault_prepare","description":"The wallet steps for a Cubicle vault as UNSIGNED transactions and EIP-712 typed data for the owner's own wallet. Open: call without vault for the transaction that opens it. Fund and hire: call again with the new vault's address (from my_vaults) for allow + deposit and the mandate to sign. Withdraw: withdraw_shares queues shares, then claim_shares pays what the vault's free funds cover to the owner. Chain 4663 is Robinhood Chain (real USDG; open to any wallet since 2026-10-07, no deposit cap; the contracts are not externally audited, so deposit only what you can afford to lose); 36927 is the devnet (test money). Signs and sends nothing.","inputSchema":{"type":"object","properties":{"owner":{"type":"string","description":"the 0x address of the wallet that owns (or will own) the vault"},"chain":{"type":"number","enum":[4663,36927],"description":"4663 Robinhood Chain (real money) or 36927 the devnet (test money); defaults to the devnet"},"vault":{"type":"string","description":"the vault's 0x address (every call after the first)"},"kind":{"type":"string","description":"what the account is for: spot (trades on Uniswap today) or futures (margin; settles when the venue sends statements)","enum":["spot","futures","funding"]},"name":{"type":"string","description":"a name for the vault"},"deposit_usd":{"type":"number","description":"USD of the stable to deposit (second call)"},"administrator":{"type":"string","description":"who administers the vault under the mandate (second call), e.g. llm-nemotron-dual"},"margin_usd":{"type":"number","description":"margin vaults only: the most the mandate may ever lose (second call)"},"market":{"type":"string","description":"spot administrators only: the market it buys","enum":["ETH","BTC"]},"withdraw_shares":{"type":"string","description":"withdrawal step one: whole shares to queue (read balanceOf(owner) on the vault)"},"claim_shares":{"type":"string","description":"withdrawal step two: whole shares to claim (read claimableRedeemRequest(0, owner) on the vault)"}},"required":["owner"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["copy"]},{"name":"workflow_read","description":"Read a trading idea the way the studio does: the deterministic reader fills the 7 parts (markets, trigger, direction, stand aside, stop, target, session) from the trader's OWN words and says which required parts are still missing. Use it before restating or forging a rule; never fill a part the trader did not say.","inputSchema":{"type":"object","properties":{"text":{"type":"string","description":"everything the trader has said about the rule, in their words"}},"required":["text"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["studio"]},{"name":"studio_move","description":"Move the trader's rule through the studio's state machine when forge is refused: score_parts (Jev scores clarity — needs the trader's own TypeSafe key), accept_parts (the trader accepts the rule as read, without a Jev score), choose_coverage with choice approximate (build from the gates the tape can measure) or stop, reset (start over). Ask the trader before accepting or choosing for them; the studio decides every gate, this only performs the move they chose.","inputSchema":{"type":"object","properties":{"move":{"type":"string","description":"the move","enum":["score_parts","accept_parts","choose_coverage","reset"]},"choice":{"type":"string","description":"for choose_coverage: approximate or stop","enum":["approximate","stop"]}},"required":["move"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["studio"]},{"name":"forge","description":"The studio machine's FORGED move: compiles the trader's strategy from the gates the tape can measure. The studio refuses it unless the strategy has passed READ, CLARIFIED and EXPRESSIBLE — when refused, tell the trader the move the studio is waiting for (it is in the result).","inputSchema":{"type":"object","properties":{"intent":{"type":"string","description":"the rule in the trader's words, every part included"}},"required":["intent"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["studio"]},{"name":"grade","description":"Grade a forge with Jev (RUBRIC_v1): code checks the facts first, then Jev answers whether the code does what the trader said. Spends one of the trader's Jev decisions (trial, plan or their own key). It judges fidelity to their words, never profitability.","inputSchema":{"type":"object","properties":{"seed_hash":{"type":"string","description":"the seed hash forge returned"}},"required":["seed_hash"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true},"security":[{"cubicleKey":[]}],"sellers":[]},{"name":"rehearse","description":"Walk a forged machine over the real tape with the canonical stepper (the last week by default): how many trades it took, the R, and a comparison with a random null. Call it after a forge when the trader wants to know whether the rule actually fires. A rehearsal is evidence about the past, never a forecast.","inputSchema":{"type":"object","properties":{"seed_hash":{"type":"string","description":"the seed hash forge returned"},"market":{"type":"string","description":"the market to walk, defaults to the one the trader named","enum":["NQ","ES","YM","BTC","ETH","SOL"]}},"required":["seed_hash"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["studio"]},{"name":"tape","description":"Read real candles from the Taifoon tape for one market. Use it to ground an answer in what the market actually did in a past window — never to predict. NQ is not continuous (weekend halt, 21:00 UTC daily break). A past window is SEALED: its bars never change, so cache it. The same tape is open over plain HTTP with no key: GET https://clob.taifoon.dev/tape/v1/candles/{market}/{tf}?from=&to= with unix SECONDS and a User-Agent that names you (OpenAPI: https://www.taifoon.io/clob/openapi.yaml). It is the tape the coordination layer's conformance check replays, so what you read here is what the judge reads.","inputSchema":{"type":"object","properties":{"market":{"type":"string","description":"the market","enum":["NQ","ES","YM","BTC","ETH","SOL"]},"tf":{"type":"string","description":"timeframe (the time machine serves these; build a 15-minute range from 1m or 5m bars)","enum":["15s","1m","5m","1h"]},"from":{"type":"string","description":"window start, ISO-8601 UTC"},"to":{"type":"string","description":"window end, ISO-8601 UTC, in the past"}},"required":["market","tf","from","to"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["candles"]},{"name":"forecast","description":"The envelope the forecast sidecar had issued for one market and timeframe by a given time: a MAGNITUDE band (how far price may travel over the next 24 bars, as two prices and a width in ATR), the odds of a move of 0.5, 1 and 2 ATR within 6, 12 and 24 bars, and its measured calibration (ece, n, band coverage). It carries NO direction — there is no side, no median, and the band is symmetric about the last close by construction; say so, and never turn it into 'up' or 'down'. When calibration reads unmeasured or uncalibrated the deck does not draw it and you should say the band is not yet trusted. Facts only, computed in code; nothing here is a recommendation.","inputSchema":{"type":"object","properties":{"market":{"type":"string","description":"the market","enum":["NQ","ES","YM","BTC","ETH","SOL"]},"tf":{"type":"string","description":"timeframe the sidecar forecasts","enum":["5m","1h"]},"at":{"type":"string","description":"ISO-8601 UTC time the envelope must have been issued by (defaults to now); for a replay, the logical clock"}},"required":["market","tf"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["candles"]},{"name":"chart_state","description":"What the deck's chart would show for one market, timeframe and window, as ONE JSON object computed in code: the bars (count, first and last, the last candle), the indicators drawn, the trader's own marked levels and zones (theirs — they may be used as levels the trader gave), the envelope under its draw gate and its facts, and the fact sheet of the window (open, close, high, low, range, change, volume, session). Use it instead of reading pixels: every number is measured, not read. Past windows only; at most 600 bars.","inputSchema":{"type":"object","properties":{"market":{"type":"string","description":"the market","enum":["NQ","ES","YM","BTC","ETH","SOL"]},"tf":{"type":"string","description":"timeframe","enum":["15s","1m","5m","1h"]},"from":{"type":"string","description":"window start, ISO-8601 UTC"},"to":{"type":"string","description":"window end, ISO-8601 UTC, in the past (the logical clock for a replay)"},"indicators":{"type":"array","items":{"type":"string"},"description":"indicator ids to include, e.g. [\"ema20\",\"vwap\",\"envelope\"]; defaults to ema20 and envelope"}},"required":["market","tf","from","to"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["candles"]},{"name":"chart_image","description":"The deck's own chart plate for one market, timeframe and window, rendered server-side from the same geometry the screen uses (bars, indicators, the envelope cone when its calibration permits) as a PNG you can look at, together with the chart_state JSON for the same window. The picture is for shape; the numbers are in the JSON — quote those, never read them off the image. Past windows only; at most 600 bars.","inputSchema":{"type":"object","properties":{"market":{"type":"string","description":"the market","enum":["NQ","ES","YM","BTC","ETH","SOL"]},"tf":{"type":"string","description":"timeframe","enum":["15s","1m","5m","1h"]},"from":{"type":"string","description":"window start, ISO-8601 UTC"},"to":{"type":"string","description":"window end, ISO-8601 UTC, in the past"},"indicators":{"type":"array","items":{"type":"string"},"description":"indicator ids to draw; defaults to ema20 and envelope"}},"required":["market","tf","from","to"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["candles"]},{"name":"image","description":"Look at a chart the trader attached (the id is in the studio state). Read what is on it — instrument, timeframe, levels, the pattern they point at — and say plainly that numbers read off a picture are READ, not measured; check the ones that matter against the tape.","inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"the attached image's id"}},"required":["id"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true},"security":[{"cubicleKey":[]}],"sellers":[]},{"name":"skills","description":"List the Cubicle skill bank: every skill's name, family, grade and status (runnable / stub / concluded). A stub is a promise, not a capability — say so when you cite one.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["studio"]},{"name":"skill_read","description":"Read one skill's method (its SKILL.md) — only the methodology and engineering skills are readable; strategy, lens, pillar and copy skills are sealed and will be refused.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"the skill's name as the skills tool lists it"}},"required":["name"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false},"security":[{"cubicleKey":[]}],"sellers":["studio"]}]},"x-related":{"coordinationLayer":{"mcp":"https://coord.taifoon.dev/mcp","openapi":"https://coord.taifoon.dev/v1/openapi.json","note":"who is listed, hired on what terms, and the verdict; Cubicle's sellers are registered there with the cards at /cards/{seller}"},"openTape":{"url":"https://clob.taifoon.dev/tape/v1/candles/{market}/{tf}","openapi":"https://www.taifoon.io/clob/openapi.yaml","served_by":"the Taifoon venue, not the deck; no key","markets":["NQ","ES","YM","MNQ","MES","BTC","ETH","SOL","BTC-PERP"],"timeframes":["15s","1m","5m","1h"],"query":"from and to in unix SECONDS","userAgent":"send a User-Agent that names your fetcher; the default python-urllib agent is refused with 403","sealed":"a past window is sealed: its bars never change, so cache it; bars are never the clock","cacheHeader":"X-Tape-Cache carries the origin's own verdict on the window","sessions":"NQ, ES and YM are not continuous: weekend halt and a daily break at 21:00 UTC"},"typedLane":{"openapi":"https://cubicle.algotrada.com/deck/api/typed/openapi","mcp":"https://typesafe.taifoon.dev/mcp","note":"typed decisions over TypeSafe's Jev with the caller's own key; documented by its own contract"}}}