{"openapi":"3.1.0","info":{"title":"preCrime API","version":"v1","summary":"Cases, pools, verdicts and settlements as JSON.","description":"The preCrime API. Cases are time-bounded forecasts about publicly observable events, settled on-chain through a parimutuel pool; nothing here is a finding of misconduct about anybody.\n\n**Money is always a string.** Every token amount is an integer in base units on a field whose name ends in `BaseUnits`, serialised as a decimal string, because 10^18 base units — one PRECRIME — already exceeds the range an IEEE-754 double holds exactly and a JSON number would silently round a stake in transit. Parse them with a big-integer type. Where a human-readable amount appears beside one, it is a display rendering and the base units are authoritative.\n\n**Rates are integer basis points.** 6000 is 60.00%. Implied probabilities included.\n\n**Two credentials, one header.** A bearer token prefixed `pc_` is a scoped API key and consumes a daily quota; anything else is a session token from a wallet signature. Minting a key is session-only, so a leaked key can never escalate itself, and no key can carry an operator permission.\n\n**Pagination** is by opaque positional cursor: list reads take `limit` and return `nextCursor`, and a null cursor means the end. Cursors are not offsets; do not construct one.\n\n**Errors** carry a code on `error`, and that code is the field to branch on. Internal detail is logged rather than returned, because it can name internal hosts.\n\n**Provenance is explicit.** Endpoints that report figures say where the figures came from, and a state where nothing has happened has its own name — `NO_SETTLEMENTS_YET`, `QUERY_TOO_VAGUE`, a null timestamp — rather than a zero that would read as a measurement.\n\n**This document is derived, not maintained.** Its path list comes from the route inventory the running service collects as it registers routes, the same list `GET /v1/endpoints` serves, so it cannot describe an endpoint that is not served or omit one that is. Anything served that the prose catalogue could not describe is listed on `x-precrime-documentation-gaps`."},"tags":[{"name":"Service","description":"What the service is and what it serves. Reported by the running process."},{"name":"Markets","description":"Upstream prediction-market data, read-only."},{"name":"Authentication","description":"Wallet-signature sign-in and the session it issues."},{"name":"Identity","description":"Who the presented credential is, and ending a session."},{"name":"API keys","description":"Minting, listing and revoking scoped machine credentials."},{"name":"Webhooks","description":"Delivery destinations for domain events."},{"name":"Cases","description":"The record itself: cases, their rules, pools, evidence and history."},{"name":"Evidence","description":"Submitting and reading a case's evidence. Evidence informs a reader; it never decides a verdict."},{"name":"Search","description":"One query endpoint that distinguishes a vague query from an empty result."},{"name":"Aggregates","description":"Rankings and protocol accounting, derived from records the product holds."},{"name":"Account","description":"The signed-in user's own reputation, activity and referral earnings."},{"name":"Watchlist","description":"Addresses a user has chosen to follow."},{"name":"Moderation","description":"Operator-only review of reports, cases and disputes. Every action records a written reason, and none of them moves money — the pool is on chain and only a MANUAL_REVIEW_ROLE holder can void it."},{"name":"Operations","description":"Operator-only queue inspection and repair. Never reachable with an API key."},{"name":"Payments","description":"Sponsor slots and API plans bought in dollar stablecoins through the on-chain payment router (decision D-029). The server prices every order and signs the price; the router checks the signature; and only the router's own events mark an order paid, settled or refunded."},{"name":"Referrals","description":"Campaign codes: a creator's named links, all rolling up to one owner and one balance (decision D-036). A code is claimed rather than assigned, and disabled rather than deleted."},{"name":"Sponsorships","description":"Applying to sponsor, and the placements live on each surface. Every placement is labelled Sponsored and carries no field by which it could ask for a position."},{"name":"Access","description":"What each tier buys and what one account holds (decisions D-025, D-027). A tier is reached by a subscription or by a PRECRIME lock observed on chain, never by a figure a client sends."},{"name":"Staking","description":"The staking vault and the first-loss backstop (decisions D-038, D-039). Balances are read from the contracts; the sequence comes from indexed logs."},{"name":"Backers","description":"The backer vault (decision D-060): dollars that take the thin side of dollar cases so they settle two-sided. Every figure is read from the contract at one block; no rate is published."},{"name":"Rewards","description":"The community campaigns that pay PRECRIME (decision D-057): seasons, events and funded drops, each address's claims with their proofs, and what has vested and been claimed, read from `CommunityRewards` at one block. No figure in dollars and no rate."},{"name":"Chain","description":"Reads of the chain for a page whose wallet is a phone: forwarded to this deployment's RPC, reads only, one request at a time."},{"name":"Feed","description":"What preCrime has on a token, for a terminal, a launchpad or a bot: public, callable from any site, cached for fifteen seconds, and read from indexed rows so it never reaches the chain."},{"name":"Launches","description":"New tokens on this chain's venues — Pons, and Uniswap v2, v3 and v4 — as the factories' own logs announced them, so a creator can find one to open a case on. Public, read from indexed rows, never from the chain."},{"name":"Points","description":"The season's live points (docs/points.md): the worker's hourly computation from indexed rows, read from its table. While the season's weights are sealed each source is in its own unit and there is no total; the season's build, not this table, decides what it pays."},{"name":"Partners","description":"Launchpads, KOLs and groups with an arrangement: an application naming codes the account already holds, the partner's own figures, and the operator's review. Nothing here moves a token."},{"name":"Prices","description":"Chainlink's feeds on this chain as the API last read them, the US equity market calendar a Stock Token price case pins, and Hyperliquid's perps as context (decision D-082). For reading only: a price case settles from the resolver's own reads at finalised blocks, and Hyperliquid's prices never settle anything."}],"paths":{"/health":{"get":{"operationId":"getHealth","summary":"Liveness","description":"Answers only whether this process is up and serving. It is deliberately not a readiness check: it does not touch the database, the queue or any provider, so a 200 here says nothing about whether a settlement can be written.","tags":["Service"],"responses":{"200":{"description":"The process is serving.","content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["ok"]}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/access/tiers":{"get":{"operationId":"getAccessTiers","summary":"What each tier buys, and the lock that reaches it","description":"The catalogue, and only the catalogue: it never says what the caller holds. Each tier carries the lock that reaches it, its features and its daily API allowance — and `apiRequestsPerDay` is null with `apiStatus: NOT_CONFIGURED` when nobody has set a figure, which is a different fact from a configured zero meaning the tier has no API access. The locker discount is read from the factory on chain and cached for five minutes; it is NOT_CONFIGURED with no lock deployed and UNAVAILABLE when the factory could not be read, because a rate invented here would appear on a pricing page as a decision somebody made.","tags":["Access"],"responses":{"200":{"description":"The tier catalogue and the lock contract's live state.","content":{"application/json":{"schema":{"type":"object","required":["tiers","lockerDiscount","lockContract","lockContractStatus"],"properties":{"tiers":{"type":"array","items":{"type":"object","required":["tier","subscriptionPlan","lock","apiRequestsPerDay","apiStatus"],"properties":{"tier":{"type":"string","enum":["EXPLORER","BUILDER","RESEARCH","DESK"]},"subscriptionPlan":{"type":"string"},"lock":{"type":["object","null"],"description":"The PRECRIME lock that reaches this tier, or null for the floor.","additionalProperties":true},"apiRequestsPerDay":{"type":["integer","null"],"description":"Null means nobody configured one. Zero means no API access."},"apiStatus":{"type":"string","enum":["CONFIGURED","NOT_CONFIGURED"]},"features":{"type":"array","description":"What the tier adds that something enforces: LIVE_ALERTS (an alert when a case opens on an address or token the account watches) and WEBHOOKS.","items":{"type":"string","enum":["LIVE_ALERTS","WEBHOOKS"]}}}}},"lockerDiscount":{"oneOf":[{"type":"object","required":["status","rebateBps","effectiveFeeBps"],"properties":{"status":{"type":"string","enum":["CONFIGURED"]},"rebateBps":{"type":"integer"},"effectiveFeeBps":{"type":"integer"},"totalFeeBps":{"type":"integer"},"rungCaps":{"type":"array","description":"The discount cap for rungs 1 to 4, in the caps asset's base units, as strings.","items":{"$ref":"#/components/schemas/BaseUnits"}},"capsAsset":{"$ref":"#/components/schemas/EvmAddress"}}},{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["NOT_CONFIGURED","UNAVAILABLE"]}}}]},"lockContract":{"type":["object","null"],"additionalProperties":true},"lockContractStatus":{"type":"string","enum":["READ","UNAVAILABLE","NOT_CONFIGURED"],"description":"READ means the figures came from the chain. The other two are not zero figures."}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/auth/challenge":{"post":{"operationId":"createAuthChallenge","summary":"Start a wallet sign-in","description":"Issues a nonce and the exact message to sign. The message binds the signature to this deployment's origin and chain id, so a signature collected by another site cannot be replayed here.","tags":["Authentication"],"requestBody":{"description":"The address that will sign.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["address"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"}}}}}},"responses":{"201":{"description":"A challenge to sign.","content":{"application/json":{"schema":{"type":"object","required":["nonce","message","expiresAt"],"properties":{"nonce":{"type":"string","description":"Return this with the signature."},"message":{"type":"string","description":"Sign exactly this. Altering a character invalidates the proof."},"expiresAt":{"$ref":"#/components/schemas/Timestamp"}}}}}},"400":{"description":"`INVALID_REQUEST`: the address is not an EVM address. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/auth/telegram":{"post":{"operationId":"signInWithTelegram","summary":"Exchange Telegram initData for a session","description":"Verifies the `initData` a Telegram Mini App hands the page, server-side, against the bot token, and only then issues a session. The verification is the whole point: a Telegram user id supplied by a client is not an identity, and this route is what makes the Mini App's claim to treat every visitor as anonymous true rather than aspirational. The session it returns carries no wallet — `walletAddress` is present and null rather than omitted, so a client reading both sign-in responses the same way sees that none is bound instead of that the field was forgotten. `telegramId` is decimal text, not a JSON number: Telegram ids are 64-bit, and emitting one as a number is a silent narrowing that can land on another id.","tags":["Authentication"],"requestBody":{"description":"The raw `initData` query string exactly as Telegram supplied it. Re-encoding it breaks the signature, which is computed over the bytes Telegram sent.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["initData"],"properties":{"initData":{"type":"string","description":"Verbatim from `Telegram.WebApp.initData`. Do not parse and re-serialise."}}}}}},"responses":{"201":{"description":"A session bound to a verified Telegram user.","content":{"application/json":{"schema":{"type":"object","required":["token","expiresAt","userId","telegramId","walletAddress","accountCreated","verifiedBy"],"properties":{"token":{"type":"string","description":"Send as `Authorization: Bearer`. Not prefixed `pc_` — that prefix means a key."},"expiresAt":{"$ref":"#/components/schemas/Timestamp"},"userId":{"type":"string","description":"The account this Telegram user resolved to."},"telegramId":{"type":"string","pattern":"^[0-9]+$","description":"Decimal text, because a 64-bit id does not survive a JSON number."},"walletAddress":{"type":"null","description":"Always null here. A Telegram session has no wallet bound to it."},"accountCreated":{"type":"boolean","description":"True when this sign-in created the account rather than resolving one."},"verifiedBy":{"type":"string","description":"Which scheme actually proved this, so a log can say what was checked."}}}}}},"400":{"description":"`INIT_DATA_MALFORMED`: the string is not a readable `initData`. Most often it was parsed and re-serialised somewhere, which changes the bytes the signature covers. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`INIT_DATA_BAD_SIGNATURE` when the HMAC does not match this bot token, or `INIT_DATA_EXPIRED` when `auth_date` is older than the accepted age. The age bound is what stops a captured `initData` replaying forever; a valid signature alone would not.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`ACCOUNT_NOT_ACTIVE`: the linked preCrime account is suspended or deleted. The status is named because the holder has just proved they are this Telegram user, so this tells them about their own account rather than disclosing it to a stranger.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`SIGN_IN_RACED`: another sign-in for this Telegram user was completing simultaneously. Retry; the second attempt resolves the account the first created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`INIT_DATA_NO_USER`: the payload verified but names no user. There is nothing to sign in.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`TELEGRAM_NOT_CONFIGURED`: no bot token is set, so no `initData` can be verified. Reported as a configuration fault rather than a rejected sign-in, because blaming the visitor for a missing setting sends them to fix something they do not control.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/auth/verify":{"post":{"operationId":"verifyAuthChallenge","summary":"Exchange a signature for a session","description":"Verifies the signature against the challenge and issues a session token. A referral code may be supplied and is attributed here and nowhere else, because this is the first moment an identity has been proved and a referral recorded on an unverified visitor would be trivially farmable. Attribution failing never fails the sign-in: `referralAttributed` reports what actually happened. A challenge that has expired, or that was issued for another origin or chain, is refused with a 401 before the signature is checked.","tags":["Authentication"],"requestBody":{"description":"The nonce, the signature over the challenge message, and optionally the referral code the visitor arrived with.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["nonce","signature"],"properties":{"nonce":{"type":"string","description":"From the challenge."},"signature":{"type":"string","pattern":"^0x[0-9a-fA-F]+$","description":"The wallet's signature over the challenge message."},"referralCode":{"type":"string","description":"The code the visitor arrived with, if any."}}}}}},"responses":{"201":{"description":"A session.","content":{"application/json":{"schema":{"type":"object","required":["token","expiresAt","userId","walletAddress","referralAttributed"],"properties":{"token":{"type":"string","description":"Send as `Authorization: Bearer`. Not prefixed `pc_` — that prefix means a key."},"expiresAt":{"$ref":"#/components/schemas/Timestamp"},"userId":{"type":"string","description":"The account this wallet resolved to."},"walletAddress":{"$ref":"#/components/schemas/EvmAddress"},"referralAttributed":{"type":"boolean","description":"Whether a referral was actually attributed to this sign-in. False means none was, for any reason — not that none was offered."}}}}}},"400":{"description":"`INVALID_REQUEST`: the nonce, signature or referral code is malformed. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNKNOWN_CHALLENGE` for a nonce that was never issued, `CHALLENGE_CONSUMED` for one already exchanged, `EXPIRED` for one whose ten minutes ran out before the signature arrived — a slow wallet — `DOMAIN_MISMATCH` or `CHAIN_MISMATCH` for one issued for another origin or chain, or `BAD_SIGNATURE` when the signature does not recover to the challenged address. None is retryable with the same input; start again at the challenge.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`WALLET_ALREADY_LINKED`: the wallet is bound to a different account than the one the challenge was issued for, and a fresh signature does not re-point it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/backers":{"get":{"operationId":"getBackers","summary":"The backer vault, live, with its open positions","description":"Every figure is read from the vault at one block. `valueBaseUnits` is the contract's `value()`: the dollars it holds for backers plus open positions at what they cost, so while `valueIsEstimate` is true it is an estimate, and at a roll, with nothing open, it is exact. Each open position carries the case it belongs to, joined by pool address, or null ids when this deployment knows no case for that pool — the position is listed either way, because the chain holds it. `rolls` are the prices past rolls fixed, newest first; `yieldRate` is always null, because what backers earn depends on fees not yet paid and verdicts not yet reached. 503 NOT_CONFIGURED when no vault address is set, which is a different statement from an empty vault, and 503 UNAVAILABLE when the vault could not be read. `instant` is the opted-in instant reserve (G-003): none of its dollars is in `valueBaseUnits`, plain deposits never reach it, and each of its figures is read on its own and is `UNAVAILABLE` when it could not be read, never a zero. `sleeve` is null until the timelock names one (T11), and until then `optIn` is `CLOSED`.","tags":["Backers"],"responses":{"200":{"description":"The vault, its limits, its open positions and its past roll prices.","content":{"application/json":{"schema":{"type":"object","required":["vault","limits","positions","rolls","instant","yieldRate"],"properties":{"vault":{"type":"object","required":["address","asset","valueBaseUnits","valueIsEstimate","epoch","epochEnd","priceScale","lastRollPrice","totalSharesBaseUnits","paused","blockNumber"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"asset":{"allOf":[{"$ref":"#/components/schemas/EvmAddress"}],"description":"USDG, the vault's only asset."},"assetSymbol":{"type":["string","null"]},"assetDecimals":{"type":["integer","null"]},"valueBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"valueIsEstimate":{"type":"boolean","description":"True while any position is open and so counted at its cost."},"epoch":{"type":"integer","description":"The epoch now taking requests."},"epochEnd":{"type":"string","format":"date-time","description":"When the epoch may roll. It rolls only once every position is collected."},"epochLengthSeconds":{"type":"integer"},"priceScale":{"type":"string","description":"The contract's PRICE_SCALE, 10^18: a price is per 10^18 shares."},"lastRollPrice":{"type":["string","null"],"pattern":"^\\d+$","description":"USDG base units per 10^18 shares, fixed by the latest roll. Null before the first roll."},"totalSharesBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"pendingDepositsBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"pendingWithdrawSharesBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"reservedForWithdrawalsBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"deployedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"paused":{"type":"boolean","description":"Stops deposits and new backing. Withdrawals, collection and the roll never pause."},"blockNumber":{"type":"string"}}},"limits":{"type":"object","required":["depositCapBaseUnits","depositRoomBaseUnits","maxPerPoolBaseUnits","perPoolBps","perPoolRoomBaseUnits","maxDeployedBps"],"properties":{"depositCapBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"depositRoomBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"How much more may be deposited now, as `deposit()` computes it."},"maxPerPoolBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"perPoolBps":{"$ref":"#/components/schemas/Bps"},"perPoolRoomBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"The smaller of the two per-pool limits: what one new position may take."},"maxDeployedBps":{"allOf":[{"$ref":"#/components/schemas/Bps"}],"description":"The most of the vault's value that may sit in open pools at once."}}},"positions":{"type":"array","items":{"type":"object","required":["pool","side","costBaseUnits","caseId","caseTitle"],"properties":{"pool":{"$ref":"#/components/schemas/EvmAddress"},"side":{"oneOf":[{"$ref":"#/components/schemas/PositionSide"},{"type":"null"}],"description":"The side the vault took: always the thinner one when it did."},"costBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"caseId":{"type":["string","null"]},"caseTitle":{"type":["string","null"]}}}},"rolls":{"type":"array","description":"At most 52, newest first.","items":{"type":"object","required":["epoch","price"],"properties":{"epoch":{"type":"integer"},"price":{"type":"string","pattern":"^\\d+$","description":"USDG base units per 10^18 shares, as `epochPrice(epoch)` holds it."}}}},"instant":{"type":"object","required":["sleeve","optIn","reserveBps","budgetShareBps","sleeveEpochCapBaseUnits","reserve","sleeveLimits"],"properties":{"sleeve":{"oneOf":[{"$ref":"#/components/schemas/EvmAddress"},{"type":"null"},{"type":"string","const":"UNAVAILABLE"}],"description":"The `InstantSleeve` the vault names, the only contract that can draw a reserve budget. Null while none is named (L2 until T11)."},"optIn":{"type":"string","enum":["OPEN","CLOSED","UNAVAILABLE"],"description":"Whether `depositOptedIn` would be taken: OPEN exactly when a sleeve is named and `reserveBps` is above zero."},"reserveBps":{"oneOf":[{"$ref":"#/components/schemas/Bps"},{"type":"string","const":"UNAVAILABLE"}],"description":"The share of an opted-in deposit sent to the reserve, fixed at the deposit. At most 500."},"budgetShareBps":{"oneOf":[{"$ref":"#/components/schemas/Bps"},{"type":"string","const":"UNAVAILABLE"}],"description":"The most of the reserve one weekly budget may take. At most 5,000 (half)."},"sleeveEpochCapBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"string","const":"UNAVAILABLE"}],"description":"The most one weekly budget may be. At most 1,000 USDG."},"reserve":{"oneOf":[{"type":"object","required":["totalShareUnits","cashBaseUnits","budgetBaseUnits","heldBaseUnits","sleeveOut","nextRollPrice"],"properties":{"totalShareUnits":{"type":"string","pattern":"^\\d+$","description":"Reserve share units: 10^6 a base unit at the price of an empty reserve."},"cashBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"budgetBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"Set aside by the last roll and not yet drawn by the sleeve."},"pendingDepositsBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"pendingWithdrawShareUnits":{"type":"string","pattern":"^\\d+$"},"heldBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"`reserveHeld()`: every reserve dollar in the vault."},"sleeveOut":{"type":"boolean","description":"The sleeve drew a budget and has not come back flat: nobody joins the reserve and no new budget is set until it does."},"sleeveNamedEpoch":{"type":"integer"},"nextRollPrice":{"type":"string","pattern":"^\\d+$","description":"USDG base units per 10^18 reserve share units, by the roll's own arithmetic, from the cash in the vault: what the sleeve still has out counts for nothing until it comes back."}}},{"type":"string","const":"UNAVAILABLE"}]},"sleeveLimits":{"oneOf":[{"type":"object","required":["perMarketBaseUnits","perDayBaseUnits","perAccountDayBaseUnits"],"properties":{"perMarketBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"perDayBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"perAccountDayBaseUnits":{"$ref":"#/components/schemas/BaseUnits"}}},{"type":"null"},{"type":"string","const":"UNAVAILABLE"}],"description":"The named sleeve's own caps, read from it. Null while none is named."}}},"yieldRate":{"type":"null","description":"Always null: nothing that would set a rate has happened yet."}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: no vault address is set. `UNAVAILABLE`: the vault could not be read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/backstop":{"get":{"operationId":"listBackstopIncidents","summary":"Every backstop incident, newest first","description":"A PREPARED incident is one whose entries exist here and which the chain has not been asked for; nothing claimable stands behind it yet, and the status says so rather than implying money is waiting. 503 NOT_CONFIGURED when no claims contract is set.","tags":["Staking"],"responses":{"200":{"description":"The incidents.","content":{"application/json":{"schema":{"type":"object","required":["incidents"],"properties":{"incidents":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: `BACKSTOP_CLAIMS_ADDRESS` is unset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/backstop/{incidentId}":{"get":{"operationId":"getBackstopIncident","summary":"One incident, with the proof that spends it","description":"The chain's record and this deployment's sit side by side and are never merged. A proof is returned only when `proofStatus` is SPENDABLE, which needs three things at once: the incident open on chain, the chain's root equal to the root these entries rebuild to, and this entry's stored proof still verifying against it. Anything else returns the amount without the proof, because a proof nobody can spend reads as money owed and ends in a reverted transaction. A root that disagrees still answers 200, with both roots shown and `proofStatus: ROOT_MISMATCH`: it is a fact about the incident, not a fault in the request.","tags":["Staking"],"parameters":[{"name":"incidentId","in":"path","required":true,"description":"The bytes32 the contract keys the incident by.","schema":{"type":"string","pattern":"^0x[0-9a-fA-F]{64}$"}},{"name":"account","in":"query","required":false,"description":"The address to look up a claim for. The contract pays only this address.","schema":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"}}],"responses":{"200":{"description":"The incident, and the caller's claim if one was asked for.","content":{"application/json":{"schema":{"type":"object","required":["incidentId","onChain","recorded","proofStatus"],"properties":{"incidentId":{"type":"string"},"onChain":{"type":["object","null"],"additionalProperties":true},"recorded":{"type":["object","null"],"additionalProperties":true},"proofStatus":{"type":"string","enum":["SPENDABLE","NOT_OPENED","ROOT_MISMATCH","ENTRIES_DO_NOT_REBUILD","NOT_RECORDED","CLAIM_PERIOD_OVER"]},"claim":{"type":["object","null"],"additionalProperties":true}}}}}},"400":{"description":"`INVALID_INCIDENT_ID` or `INVALID_ACCOUNT`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: neither the chain nor this deployment knows that incident.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: `BACKSTOP_CLAIMS_ADDRESS` is unset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/case-factory":{"get":{"operationId":"getCaseFactoryTerms","summary":"What the case factory offers a new case","description":"The stake-asset allowlist with each asset's base ceiling, exposure bound and open exposure, the fee schedule a new pool is built with, the locker rebate, the ceiling policy and the shortest challenge window a new case may choose — all read from the factory on chain (decision D-020) and held for up to a minute. The wizard's asset step, its fee box and its lightning preset's challenge window (decision D-084) are built from this. Assets are listed by address; a symbol is only a label, and is null when the token's own answer is unfit to print.","tags":["Cases"],"responses":{"200":{"description":"The factory's terms.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseFactoryTerms"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`CHAIN_NOT_CONFIGURED` when no case factory or RPC endpoint is set; `CHAIN_UNAVAILABLE` when the factory could not be read; `CHAIN_MISMATCH` when the RPC serves another chain, where the factory's address is somebody else's contract. Never an empty list: that would claim the factory lists nothing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/case-templates":{"get":{"operationId":"listCaseTemplates","summary":"The resolution-template catalogue","description":"Every case type the server recognises, with the fields its wizard renders and whether this deployment can settle it (decision D-035). A type that needs a feed the operator has not configured, a venue nobody has verified on the chain, or an evaluator this build does not have is listed with `resolvable.ok: false` and the reason; drafting one answers 422 `NOT_RESOLVABLE`.","tags":["Cases"],"parameters":[{"name":"chainId","in":"query","description":"The chain to assess. Omitted, Robinhood Chain mainnet (4663).","schema":{"type":"integer"}}],"responses":{"200":{"description":"The catalogue.","content":{"application/json":{"schema":{"type":"object","required":["data","lightning"],"properties":{"data":{"type":"array","items":{"type":"object","required":["caseType","label","question","useCase","fields","resolvable"],"properties":{"caseType":{"type":"string","description":"The template's enum name."},"label":{"type":"string","description":"Its human label."},"question":{"type":"string","description":"What the case asks, in one line."},"useCase":{"type":"string","description":"Why someone would open one."},"publicRecord":{"type":"boolean","description":"Settled from an official public record rather than chain state."},"fields":{"type":"array","description":"The wizard's fields, in order: `name`, `label`, `kind`, `help`, `required`, and where they apply `placeholder`, `options`, `min`, `max` and `defaultValue`.","items":{"type":"object","additionalProperties":true}},"stakeFirst":{"type":"string","enum":["REQUIRED","DEFAULT"],"description":"Whether a new case of this type must count only what happens after staking closes (`REQUIRED`: a window from the case's opening answers 400 `WINDOW_NOT_STAKE_FIRST`), or starts from that and may count from opening (`DEFAULT`). Decision D-067."},"resolvable":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"},"code":{"type":"string","enum":["PROVIDER_NOT_CONFIGURED","NO_EVALUATOR","NO_VENUES_FOR_CHAIN","VENUE_UNCONFIRMED"]},"detail":{"type":"string","description":"Why it cannot settle, when it cannot."},"missingEnvVars":{"type":"array","items":{"type":"string"},"description":"The variables an operator would set, for an unconfigured feed."}}},"lightning":{"type":"object","required":["offered","reason","stakeSeconds","countSeconds","deadlineSlackSeconds","venues","feedsFromRegistry","postedBy"],"description":"Whether the wizard's Pace switch offers a lightning round on this type here (decision D-084; docs/lightning-rounds.md 2.2): on the table and settleable in this deployment. Open from day one whether or not the resolver posts the type on its own; the challenge window is the factory's floor, from `GET /v1/case-factory`.","properties":{"offered":{"type":"boolean"},"reason":{"type":["string","null"],"description":"Why not, as the wizard shows the switch greyed. Null when offered."},"stakeSeconds":{"type":"integer","description":"Staking: 900."},"countSeconds":{"type":"array","items":{"type":"integer"},"description":"The counting choices after staking closes, the first the default: 900 or 1,800, or 1,800 only for a price case and a price threshold. Empty when not offered."},"deadlineSlackSeconds":{"type":"integer","description":"The deadline after counting stops: 7,200."},"venues":{"type":["array","null"],"items":{"type":"string"},"description":"The only venues a lightning case of this type may name (v2 pairs and Pons curves for the liquidity types), or null for any it offers."},"feedsFromRegistry":{"type":"boolean","description":"A price case: only the feeds whose own `lightning.offered` is true, below."},"postedBy":{"type":["string","null"],"description":"Display only, never a gate: who posts this type's verdicts now, from the resolver's own record (the mode it tracked the newest case of the type in). `RESOLVER`, or `OPERATOR` while the type shadows; null with nothing on record."}}},"feeds":{"type":"array","description":"A price case's and a Stock Token case's registry feeds (D-082), each with its facts and `lightning`: `offered`, `reason`, and `measured`, the dated 30-minute reading of docs/lightning-rounds.md 2.3 where one was made, null where none was.","items":{"type":"object","additionalProperties":true}}}}},"lightning":{"type":"object","required":["stakeSeconds","deadlineSlackSeconds","finality"],"description":"What every lightning answer shares (decision D-084): staking 900 seconds, the deadline 7,200 after counting stops, and the chain's MEASURED finality — how long after a window ends its verdict can be posted — which the wizard, the card and the bot estimate the verdict and claims from. `finality` is null on a chain nobody measured, so no estimate is shown rather than a guess.","properties":{"stakeSeconds":{"type":"integer"},"deadlineSlackSeconds":{"type":"integer"},"finality":{"type":["object","null"],"properties":{"lowestSeconds":{"type":"integer"},"typicalSeconds":{"type":"integer"},"highestSeconds":{"type":"integer"},"measuredOn":{"type":"string"}}}}}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/cases":{"get":{"operationId":"listCases","summary":"List cases","description":"Public: a case, its rule and its evidence are inspectable by anyone, including people who never connect a wallet. Drafts are excluded unless explicitly asked for, because a list of unpublished ideas belongs to its creator. `rule.meaning` and `rule.source` are absent here — the narrative is computed only for the dossier read.","tags":["Cases"],"parameters":[{"name":"status","in":"query","description":"Restrict to one status. Omitted, every status but `DRAFT` is returned.","schema":{"$ref":"#/components/schemas/CaseStatus"}},{"name":"caseType","in":"query","description":"Restrict to one resolution template, by the `caseType` name `GET /v1/case-templates` lists. Not validated against that list before it reaches the query.","schema":{"type":"string"}},{"name":"priceClass","in":"query","description":"Price cases of one class (D-082): `CRYPTO`, `DOLLAR` (USDC and USDT depegs) or `EQUITY` (Robinhood's Stock Tokens), matched on the rule's registry feed key. Implies `caseType=PRICE_MOVE`, whatever `caseType` says.","schema":{"type":"string","enum":["CRYPTO","DOLLAR","EQUITY"]}},{"name":"target","in":"query","description":"Restrict to one target address. Lower-cased before matching and not otherwise validated: a value that is not an address simply matches nothing.","schema":{"type":"string"}},{"name":"lightning","in":"query","description":"`true` lists lightning rounds only, `false` none of them (decision D-084), by the flag the server set at publish from each pool's own times. With `sort=ending_soon`, open ones soonest to close first. Anything else is a 400.","schema":{"type":"string","enum":["true","false"]}},{"name":"sort","in":"query","description":"`newest` (default), `ending_soon`, or `most_conviction`.","schema":{"type":"string","enum":["newest","ending_soon","most_conviction"]}},{"name":"limit","in":"query","description":"Page size, 1 to 100. Defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","description":"The `nextCursor` from the previous page. Cursors are opaque and positional rather than offsets: do not construct one, and do not assume two pages taken minutes apart line up.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of cases.","content":{"application/json":{"schema":{"type":"object","required":["data","nextCursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Case"}},"nextCursor":{"type":["string","null"],"description":"Pass back as `cursor`. Null means this was the last page."}}}}}},"400":{"description":"`INVALID_REQUEST`: a query parameter failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/cases/{id}":{"get":{"operationId":"getCase","summary":"One case, in full","description":"The whole public record for a case: the rule, the live pool, the settlement if there is one, the evidence and the event history. The published rule is re-hashed against the on-chain commitment on every read and the result is returned as `ruleVerified`, so a divergence is reported rather than papered over with prose that no longer matches. A draft answers 404 to anyone.","tags":["Cases"],"parameters":[{"name":"id","in":"path","required":true,"description":"The public case id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The case dossier.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseDossier"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/cases/{id}/evidence":{"get":{"operationId":"listCaseEvidence","summary":"The evidence record of a case, as the public sees it","description":"Anonymous, because a verdict nobody can re-derive is a verdict nobody should believe. Sealed and unpublished material is withheld and counted in `withheld`, so a reader can see that something is held back without seeing what it is. An unpublished case answers 404, as `GET /v1/cases/{id}` does: a listing that answered 200 with everything withheld would confirm the existence of a draft that route deliberately hides.","tags":["Evidence"],"parameters":[{"name":"id","in":"path","required":true,"description":"The public case id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The record, with what this reader may not see counted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvidenceListing"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/cases/{id}/recording":{"get":{"operationId":"getCaseRecording","summary":"An account market's recording, as both recorders stored it","description":"Both recorders' latest bodies, as stored, each rehashed here to its recorder's fingerprint and to the agreed fingerprint both must share (decision D-035, amended: the one type whose verdict rests on preCrime's own recording), the fills both hold that qualify for the rule's event with a link to Hyperliquid's explorer, and the report's `recording` member once there is one. The rows are append-only. 404 for every other type.","tags":["Cases"],"parameters":[{"name":"id","in":"path","required":true,"description":"The public case id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The recording.","content":{"application/json":{"schema":{"type":"object","required":["source","account","recordings","agreedFingerprint","agreedEvents"],"properties":{"source":{"type":"string"},"account":{"$ref":"#/components/schemas/EvmAddress"},"coin":{"type":"string"},"side":{"type":"string"},"event":{"type":"string"},"openedByOwner":{"type":"boolean"},"recordings":{"type":"array","items":{"type":"object","additionalProperties":true}},"agreedFingerprint":{"type":["string","null"]},"agreedEvents":{"type":"array","items":{"type":"object","additionalProperties":true}},"report":{"type":["object","null"],"additionalProperties":true},"note":{"type":"string"}}}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/cases/{id}/resolution":{"get":{"operationId":"getCaseResolution","summary":"The resolver's record of one case, and whether the digests agree","description":"Public, because the point of committing a digest on chain is that a stranger can check it. `onChainDigest` is what the indexer read out of the pool's own `VerdictPosted` log; `evidenceDigest` is what the resolver computed from the report below. Equal means the published report is the one the pool settled against, and `digestsMatch` is null rather than false while either is missing, because \"not posted yet\" and \"posted against another report\" are different facts. A case the resolver never tracked answers `NOT_TRACKED` with no numbers, not 404.","tags":["Cases"],"parameters":[{"name":"id","in":"path","required":true,"description":"The public case id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The resolution record.","content":{"application/json":{"schema":{"type":"object","required":["status","onChainDigest","digestsMatch","items"],"properties":{"status":{"type":"string","description":"The resolver's state for this case, or NOT_TRACKED when it has no record of it."},"mode":{"type":["string","null"],"description":"SHADOW or POST, null when not tracked."},"detail":{"type":"string","description":"Present on NOT_TRACKED: why there is no record."},"head":{"type":["object","null"],"description":"The block every read was pinned to, with its hash and the tag it came from.","additionalProperties":true},"verdict":{"type":["string","null"],"description":"GUILTY or INNOCENT once decided."},"evidenceDigest":{"type":["string","null"]},"onChainDigest":{"type":["string","null"]},"digestsMatch":{"type":["boolean","null"]},"report":{"type":["object","null"],"additionalProperties":true},"items":{"type":"array","description":"What the verdict depended on, each with its block and transaction.","items":{"type":"object","additionalProperties":true}},"postTxHash":{"type":["string","null"]},"postedAt":{"type":["string","null"],"format":"date-time"},"attempts":{"type":"integer"},"blocked":{"type":["object","null"],"description":"Why it cannot decide yet, in provider terms.","additionalProperties":true},"secondaryDigest":{"type":"string","description":"The second RPC's digest, or NOT_CONFIGURED when no second endpoint was set: an endpoint nobody configured has not agreed, it was not asked."},"updatedAt":{"type":"string","format":"date-time"}}}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/cases/{id}/stake-check":{"get":{"operationId":"checkStakeAddress","summary":"Whether preCrime will prepare a stake on a case from an address","description":"On an account market, never from the account, an address Hyperliquid links to it (its master, sub-accounts, vault leader or API wallets), or, on a case its owner opened, the wallet that opened it: those answer 403 `INSIDER_ADDRESS` with the relation (decision D-082). Decided on the server from what it holds and compared lower case. The pool itself takes a stake from any address; stakes from linked addresses that reach it are listed on the case. Every other type answers `ok: true`.","tags":["Cases"],"parameters":[{"name":"id","in":"path","required":true,"description":"The public case id.","schema":{"type":"string"}},{"name":"address","in":"query","required":true,"description":"The address a stake would come from.","schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"200":{"description":"preCrime will prepare a stake from this address.","content":{"application/json":{"schema":{"type":"object","required":["ok","address"],"properties":{"ok":{"type":"boolean","const":true},"address":{"$ref":"#/components/schemas/EvmAddress"}}}}}},"400":{"description":"`INVALID_REQUEST`: `address` is not a 0x address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`INSIDER_ADDRESS`: the address is linked to the account; the message says how.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/context/hyperliquid":{"get":{"operationId":"getHyperliquidContext","summary":"Hyperliquid's perp for a coin, as context beside a crypto price case","description":"Hyperliquid's main-dex perp for a coin (`coin`) or for a registry feed's coin (`feed`): mark, oracle, funding per hour, the 24-hour change against `prevDayPx`, 24-hour volume and open interest, when it was read, and a link. Labelled as context that cannot settle any case (decision D-065, amended). One `metaAndAssetCtxs` read serves every coin, held 30 seconds. Its states, never a zero or a remembered number: `NOT_CONFIGURED` without `HYPERLIQUID_INFO_URL`; `UNAVAILABLE` when the read fails or is over two minutes old; `NO_MARKET` when the coin is missing or delisted, and for every depeg and Stock Token feed.","tags":["Prices"],"parameters":[{"name":"coin","in":"query","description":"A main-dex perp name exactly as Hyperliquid spells it (`BTC`, `kPEPE`).","schema":{"type":"string"}},{"name":"feed","in":"query","description":"A registry feed key instead of `coin`: the card for its coin, worded for it.","schema":{"type":"string"}}],"responses":{"200":{"description":"The card, in one of its states.","content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["OK","NOT_CONFIGURED","UNAVAILABLE","NO_MARKET"]},"coin":{"type":["string","null"]},"detail":{"type":"string"},"market":{"type":"string"},"markPx":{"type":"string"},"oraclePx":{"type":["string","null"]},"fundingPerHour":{"type":["string","null"]},"prevDayPx":{"type":["string","null"]},"change24hBps":{"type":["integer","null"]},"dayNotionalVolumeUsd":{"type":["string","null"]},"openInterest":{"type":["string","null"]},"readAt":{"type":"string"},"link":{"type":"string"},"label":{"type":"string"}}}}}},"400":{"description":"`INVALID_REQUEST`: give exactly one of `coin` and `feed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`FEED_NOT_LISTED`: `feed` is not a registry feed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`UNAVAILABLE` with `Retry-After`: this server has spent its share of Hyperliquid's request weight this minute (`HL_API_WEIGHT_PER_MINUTE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/endpoints":{"get":{"operationId":"listEndpoints","summary":"The service's own route list","description":"Every route this process serves, collected as the routes registered rather than maintained as a separate list. It cannot drift from what is served, which is the whole reason it is an endpoint instead of a page.","tags":["Service"],"responses":{"200":{"description":"The live route table.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Endpoint"}},"note":{"type":"string","description":"States where the list came from."}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/feed/cases":{"get":{"operationId":"casesChangedSince","summary":"Every published case that changed after a moment, oldest first","description":"For a bot that polls: give `since` for the first page, then the `cursor` each page ends with. The cursor splits cases that changed in the same instant by id, so none is skipped or repeated; it is null on an empty first page, which means ask again with the same `since`.","tags":["Feed"],"parameters":[{"name":"since","in":"query","description":"An ISO 8601 time. Required unless `cursor` is given.","schema":{"type":"string"}},{"name":"cursor","in":"query","description":"The `cursor` from the previous page.","schema":{"type":"string"}},{"name":"limit","in":"query","description":"1 to 100 cases. Defaults to 50.","schema":{"type":"integer"}},{"name":"lightning","in":"query","description":"`true` for lightning rounds only, `false` for none of them (decision D-084). Every case here carries `lightning`, the server's reading of its pool's own times at publish.","schema":{"type":"string","enum":["true","false"]}}],"responses":{"200":{"description":"The changed cases, and where to continue.","content":{"application/json":{"schema":{"type":"object","required":["cases","cursor","generatedAt"],"properties":{"cases":{"type":"array","items":{"type":"object","additionalProperties":true}},"cursor":{"type":["string","null"]},"generatedAt":{"type":"string"}}}}}},"400":{"description":"`INVALID_REQUEST`: neither `since` nor a cursor this API wrote, or a bad `lightning` filter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/feed/token/{address}":{"get":{"operationId":"tokenFeed","summary":"Every published case on a token, open ones first","description":"For a trading terminal or a launchpad to show beside a token: each case's type, status, each side's stake in the pool's own asset and the odds they imply (null for an empty pool), when staking closes, the verdict once there is one, and links to the case, the embeddable widget and the wizard. Matches a case whose target or token is the address. `Access-Control-Allow-Origin: *`; cached for fifteen seconds.","tags":["Feed"],"parameters":[{"name":"address","in":"path","required":true,"description":"The token or target address.","schema":{"type":"string"}},{"name":"chainId","in":"query","description":"Defaults to Robinhood Chain mainnet (4663).","schema":{"type":"integer"}},{"name":"limit","in":"query","description":"1 to 50 cases. Defaults to 20.","schema":{"type":"integer"}},{"name":"lightning","in":"query","description":"`true` for lightning rounds only, `false` for none of them (decision D-084). Every case here carries `lightning`, the server's reading of its pool's own times at publish.","schema":{"type":"string","enum":["true","false"]}}],"responses":{"200":{"description":"The token's cases and a summary of how they went.","content":{"application/json":{"schema":{"type":"object","required":["token","chainId","summary","cases","links","generatedAt"],"properties":{"token":{"type":"string"},"chainId":{"type":"integer"},"summary":{"type":"object","properties":{"open":{"type":"integer"},"decided":{"type":"integer"},"guiltyVerdicts":{"type":"integer"}}},"cases":{"type":"array","items":{"type":"object","additionalProperties":true}},"links":{"type":"object","additionalProperties":true},"generatedAt":{"type":"string"}}}}}},"400":{"description":"`INVALID_REQUEST`: not an address, or a bad limit, chain or `lightning` filter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/grants/bond":{"get":{"operationId":"getBondGrants","summary":"How many first-bond grants are left","description":"The first 250 creators are granted their first case's bond, once (decision D-059). Read from the grant contract: how many remain, how much each is, and when the programme closes, after which what is left can be burned by anyone. 503 NOT_CONFIGURED without the contract and its signer's key, UNAVAILABLE when the contract could not be read.","tags":["Cases"],"responses":{"200":{"description":"The programme's state.","content":{"application/json":{"schema":{"type":"object","required":["contract","remaining","grantAmountBaseUnits","closesAt"],"properties":{"contract":{"$ref":"#/components/schemas/EvmAddress"},"remaining":{"type":"integer","minimum":0},"grantAmountBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"closesAt":{"type":"string","format":"date-time"}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED` or `UNAVAILABLE`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/jar":{"get":{"operationId":"getJar","summary":"The burn jar, live, with its indexed history","description":"`NOT_CONFIGURED` carries no numbers: an empty jar and one nobody deployed are different facts. `LIVE` carries the Firepit's, the jar's and the splitters' own figures, read at one block, one entry per asset and never added across them, and the PRECRIME burned since genesis from the token's own supply. `UNAVAILABLE` means the contracts could not be read just now; the indexed history is still returned, labelled.","tags":["Aggregates"],"responses":{"200":{"description":"The jar.","content":{"application/json":{"schema":{"type":"object","required":["status","contracts","live","indexed"],"properties":{"status":{"type":"string","enum":["LIVE","UNAVAILABLE","NOT_CONFIGURED"]},"reason":{"type":"string","description":"On NOT_CONFIGURED: the unset variables."},"chainId":{"type":"integer","description":"On LIVE and UNAVAILABLE: the chain a release is sent on."},"contracts":{"type":["object","null"],"additionalProperties":true},"genesisSupplyBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"live":{"oneOf":[{"type":"object","required":["blockNumber","releaser","thresholdBaseUnits","unlockPeriodSeconds","nonce","paused","totalBurnedBaseUnits","totalSupplyBaseUnits","burnedSinceGenesisBaseUnits","assets","splitters"],"properties":{"blockNumber":{"$ref":"#/components/schemas/BlockNumber"},"releaser":{"$ref":"#/components/schemas/EvmAddress"},"thresholdBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"unlockPeriodSeconds":{"type":"integer"},"nonce":{"type":"string","pattern":"^\\d+$","description":"What a release must name. Advances on every release and retune."},"paused":{"type":"boolean"},"totalBurnedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"totalHeldBurnedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"totalSupplyBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"burnedSinceGenesisBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"heldTokenBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"assets":{"type":"array","items":{"type":"object","required":["address","symbol","decimals","heldBaseUnits","releasableBaseUnits","lockedBaseUnits","unlockEnd","totalReleasedBaseUnits"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"symbol":{"type":["string","null"]},"decimals":{"type":["integer","null"]},"heldBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"releasableBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"lockedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"unlockEnd":{"$ref":"#/components/schemas/NullableTimestamp"},"totalReleasedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"}}}},"splitters":{"type":"array","items":{"type":"object","required":["kind","address","jarBps","stakersBps","companyBps"],"properties":{"kind":{"type":"string","enum":["POOL_FEES","LIQUIDITY_FEES"]},"address":{"$ref":"#/components/schemas/EvmAddress"},"jarBps":{"$ref":"#/components/schemas/Bps"},"stakersBps":{"$ref":"#/components/schemas/Bps"},"companyBps":{"$ref":"#/components/schemas/Bps"},"minHolderBps":{"$ref":"#/components/schemas/Bps"},"jar":{"type":"string"},"stakers":{"type":"string"},"company":{"type":"string"},"paused":{"type":"boolean"}}}}}},{"type":"null"}]},"indexed":{"oneOf":[{"type":"object","required":["source","releases","burnedBaseUnits","heldBurnedBaseUnits","byAsset","distributions","lastRelease"],"properties":{"source":{"type":"string","enum":["INDEXED_CHAIN_EVENTS","NO_RELEASES_YET"]},"releases":{"type":"integer"},"burnedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"heldBurnedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"byAsset":{"type":"array","items":{"type":"object","required":["asset","inflows","inflowBaseUnits","releases","releasedBaseUnits"],"properties":{"asset":{"$ref":"#/components/schemas/EvmAddress"},"inflows":{"type":"integer"},"inflowBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"releases":{"type":"integer"},"releasedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"}}}},"distributions":{"type":"array","items":{"type":"object","required":["kind","asset","distributions","toJarBaseUnits","toStakersBaseUnits","toCompanyBaseUnits","toBackersBaseUnits"],"properties":{"kind":{"type":"string"},"asset":{"$ref":"#/components/schemas/EvmAddress"},"distributions":{"type":"integer"},"toJarBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"toStakersBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"toCompanyBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"toBackersBaseUnits":{"$ref":"#/components/schemas/BaseUnits"}}}},"lastRelease":{"type":["object","null"],"properties":{"nonce":{"type":["string","null"]},"burnedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"txHash":{"type":"string"},"occurredAt":{"$ref":"#/components/schemas/Timestamp"}}}}},{"type":"null"}]}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/jar/releases":{"get":{"operationId":"listJarReleases","summary":"Every burn through the jar, newest first","description":"Threshold releases and held-token burns, as the Firepit emitted them, with what each paid.","tags":["Aggregates"],"parameters":[{"name":"limit","in":"query","description":"Page size, 1 to 100. Defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","description":"The `nextCursor` from the previous page. Cursors are opaque and positional rather than offsets: do not construct one, and do not assume two pages taken minutes apart line up.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of releases.","content":{"application/json":{"schema":{"type":"object","required":["data","nextCursor"],"properties":{"data":{"type":"array","items":{"type":"object","required":["id","kind","nonce","caller","burnedBaseUnits","assets","txHash"],"properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["THRESHOLD_RELEASE","HELD_TOKEN_BURN"]},"nonce":{"type":["string","null"]},"caller":{"type":"string"},"recipient":{"type":["string","null"]},"burnedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"assets":{"type":"array","items":{"type":"object","required":["asset","amountBaseUnits"],"properties":{"asset":{"$ref":"#/components/schemas/EvmAddress"},"amountBaseUnits":{"$ref":"#/components/schemas/BaseUnits"}}}},"txHash":{"type":"string"},"blockNumber":{"$ref":"#/components/schemas/BlockNumber"},"occurredAt":{"$ref":"#/components/schemas/Timestamp"}}}},"nextCursor":{"type":["string","null"]}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/launch":{"get":{"operationId":"findLaunch","summary":"Who bought a token in its first blocks of trading","description":"The wizard's launch finder for sniper and bundle cases. Finds the block trading opened in — a Pons launch, or a Uniswap v2 or v3 pool's first swap after its factory created it — and every wallet that bought in the first `blocks` blocks from it, classified by the resolver's own rule, so the group shown is the group a case settles on. `bundle` is the part of it that bought in the launch block itself. Bundles are found by timing: wallets funded from one source are not checked, because native transfers leave no log and this build has no adapter that traces them here, and `funding` says so. A window that has not closed yet is refused, since a case opened on it would not know its group.","tags":["Cases"],"parameters":[{"name":"token","in":"query","required":true,"description":"The token.","schema":{"type":"string"}},{"name":"venue","in":"query","required":true,"description":"PONS_CURVE, UNISWAP_V2 or UNISWAP_V3; a v4 pool is not previewed.","schema":{"type":"string","enum":["UNISWAP_V2","UNISWAP_V3","UNISWAP_V4","PONS_CURVE"]}},{"name":"pool","in":"query","required":true,"description":"The curve or pool address.","schema":{"type":"string"}},{"name":"blocks","in":"query","description":"Blocks from the launch that count, 1 to 3,000. Defaults to 50.","schema":{"type":"integer"}},{"name":"chainId","in":"query","description":"Defaults to Robinhood Chain mainnet (4663).","schema":{"type":"integer"}}],"responses":{"200":{"description":"The launch, and every wallet that bought in its first blocks.","content":{"application/json":{"schema":{"type":"object","required":["launchBlock","foundBy","toBlock","buyers","bundle","complete","funding"],"properties":{"launchBlock":{"type":"string"},"foundBy":{"type":"string","enum":["PONS_LAUNCH_LOG","FIRST_SWAP"]},"toBlock":{"type":"string"},"totalSupply":{"type":["string","null"]},"boughtBaseUnits":{"type":"string"},"boughtShareBps":{"type":["integer","null"]},"buyers":{"type":"array","items":{"type":"object","additionalProperties":true}},"bundle":{"type":"array","items":{"type":"object","additionalProperties":true}},"complete":{"type":"boolean","description":"False when the preview did not read every log."},"detail":{"type":"string"},"funding":{"type":"object","additionalProperties":true}}}}}},"400":{"description":"`INVALID_REQUEST`: a malformed token, pool or block count.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`LAUNCH_NOT_FOUND`: no launch or pool creation for this token here.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`LAUNCH_STILL_OPEN`: the window runs past the chain's head.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`VENUE_NOT_PREVIEWED` for a v4 pool, or `POOL_NOT_PROVEN` for a pool its venue does not vouch for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`CHAIN_NOT_CONFIGURED`: no RPC or no verified venues for this chain. `CHAIN_UNAVAILABLE`: the RPC failed a read, which says nothing about the launch; retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/launches":{"get":{"operationId":"listLaunches","summary":"New tokens on this chain, newest first","description":"Every row is a launch the worker's indexer read from a factory's own log: a Pons `TokenLaunched`, or a Uniswap v2 pair, v3 pool or v4 pool pairing a token with WETH, native ETH or USDG, recognised by the registry's addresses and never by symbol. A token is listed once, at its first launch: the v4 pool a Pons token graduates into is recorded but not listed again, and with `venue` the first launch must be on that venue. `openCases` counts the cases on this chain still taking stakes whose target or token is the address. `indexer` is the checkpoint the rows come from, and null when the indexer has never run here, which is a different statement from nothing having launched. `Access-Control-Allow-Origin: *`; cached for fifteen seconds.","tags":["Launches"],"parameters":[{"name":"venue","in":"query","description":"Only tokens whose first launch was on this venue.","schema":{"type":"string","enum":["PONS","UNISWAP_V2","UNISWAP_V3","UNISWAP_V4"]}},{"name":"before","in":"query","description":"An ISO 8601 time, for launches before it, or the `cursor` from the previous page.","schema":{"type":"string"}},{"name":"limit","in":"query","description":"1 to 100 launches. Defaults to 50.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The newest launches, and where to continue.","content":{"application/json":{"schema":{"type":"object","required":["chainId","launches","cursor","indexer","generatedAt"],"properties":{"chainId":{"type":"integer"},"launches":{"type":"array","items":{"type":"object","required":["id","token","venue","pool","quoteAsset","deployer","launchedAt","blockNumber","txHash","openCases"],"properties":{"id":{"type":"string"},"token":{"$ref":"#/components/schemas/EvmAddress"},"venue":{"type":"string","enum":["PONS","UNISWAP_V2","UNISWAP_V3","UNISWAP_V4"]},"pool":{"type":"string","description":"The Pons curve, the v2 pair or v3 pool, or the v4 pool's 32-byte id."},"quoteAsset":{"type":"object","required":["address","symbol"],"properties":{"address":{"allOf":[{"$ref":"#/components/schemas/EvmAddress"}],"description":"The zero address is native ETH, in a v4 pool or on a Pons curve."},"symbol":{"type":["string","null"],"description":"`WETH`, `USDG` or `ETH`, named from the registry's address, never from the token itself."}}},"deployer":{"oneOf":[{"$ref":"#/components/schemas/EvmAddress"},{"type":"null"}],"description":"Named by Pons; null for a Uniswap pool, which records nobody."},"launchedAt":{"$ref":"#/components/schemas/Timestamp"},"blockNumber":{"type":"string","pattern":"^\\d+$"},"txHash":{"$ref":"#/components/schemas/Hash32"},"openCases":{"type":"integer","minimum":0}}}},"cursor":{"type":["string","null"],"description":"Pass as `before` for the next page. Null when nothing older is recorded."},"indexer":{"type":["object","null"],"required":["lastBlock","updatedAt"],"properties":{"lastBlock":{"type":"string","pattern":"^\\d+$"},"updatedAt":{"type":"string","format":"date-time"}}},"generatedAt":{"type":"string"}}}}}},"400":{"description":"`INVALID_REQUEST`: an unknown venue, a limit out of range, or a cursor naming no launch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/leaderboard":{"get":{"operationId":"getLeaderboard","summary":"Participants ranked by Crime Score, or creators and referrers","description":"The default board ranks detectives by accuracy, how early the call was, and how far against the consensus it went. Never by profit and never by stake size, so a large wallet cannot buy a position here. `creators` ranks by cases that actually settled, void ones excluded, with volume listed per asset rather than added across assets; `referrers` ranks by referred people who verified a wallet, with earnings per asset. Handles are derived from a referral code: no wallet, no email, nothing private. Each board returns its own row shape and says which board it is.","tags":["Aggregates"],"parameters":[{"name":"limit","in":"query","description":"Page size, 1 to 100. Defaults to 50.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"window","in":"query","description":"`all` (default) or `week`, which counts only calls settled in the last seven days.","schema":{"type":"string","enum":["all","week"]}},{"name":"board","in":"query","description":"`detectives` (default), `creators` or `referrers`. Each has its own row shape, and the response repeats which board it is.","schema":{"type":"string","enum":["detectives","creators","referrers"]}}],"responses":{"200":{"description":"The ranking. Rows carry the fields of the board that was asked for.","content":{"application/json":{"schema":{"type":"object","required":["data","board","window","note"],"properties":{"data":{"type":"array","items":{"type":"object","required":["position","handle","rank","rankLabel","callsSettled","callsCorrect","falseVisions","accuracyPct","bestStreak"],"properties":{"position":{"type":"integer","description":"1-based place in this page."},"handle":{"type":"string","description":"The public handle."},"score":{"type":"integer","description":"detectives only."},"rank":{"type":"integer","description":"detectives only."},"rankLabel":{"type":"string","description":"detectives only."},"callsSettled":{"type":"integer","description":"detectives only."},"callsCorrect":{"type":"integer","description":"detectives only."},"falseVisions":{"type":"integer","description":"detectives only: settled calls that were wrong."},"accuracyPct":{"type":"number","description":"detectives only."},"bestStreak":{"type":"integer","description":"detectives only."},"casesSettled":{"type":"integer","description":"creators only: void cases excluded."},"volume":{"type":"array","description":"creators only: gross pool per asset, never added across assets.","items":{"type":"object","additionalProperties":true}},"verified":{"type":"integer","description":"referrers only: referred people who verified a wallet."},"earnings":{"type":"array","description":"referrers only: commission per asset, from the referral ledger.","items":{"type":"object","additionalProperties":true}}}}},"board":{"type":"string","enum":["detectives","creators","referrers"],"description":"Which board these rows are."},"window":{"type":"string","enum":["all","week"],"description":"The window applied."},"note":{"type":"string","description":"States what the score does and does not measure."}}}}}},"400":{"description":"`INVALID_REQUEST`: the limit, window or board failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/market-calendar":{"get":{"operationId":"getMarketCalendar","summary":"Every released version of the US equity market calendar","description":"The versions exactly as the committed data file holds them (id, family, release date, coverage, closed intervals with their reasons, and digest), and which one a draft made now would pin. An equity price case names its version in its rule and keeps it for life; its report carries the version's id and digest, so anyone checking a verdict can fetch the version it used here and rehash it. A version is never edited or removed once released. Static data: cached for a day.","tags":["Prices"],"parameters":[{"name":"id","in":"query","description":"One version by id (`US_EQUITIES_24_5_V1`). Omitted, every version.","schema":{"type":"string"}}],"responses":{"200":{"description":"The released versions, or the one asked for.","content":{"application/json":{"schema":{"type":"object","required":["current"],"properties":{"current":{"type":["string","null"],"description":"The version a draft would pin now."},"versions":{"type":"array","items":{"type":"object","additionalProperties":true}},"version":{"type":"object","additionalProperties":true},"note":{"type":"string"}}}}}},"400":{"description":"`INVALID_REQUEST`: `id` is not a calendar version id's shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no released version has that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/markets":{"get":{"operationId":"listMarkets","summary":"Upstream prediction markets","description":"Open markets read from Polymarket's Gamma API (`POLYMARKET_GAMMA_API_URL`), fifty at a time, each with its outcome labels, its end and a link to it on Polymarket, and held for a minute. Read-only: no price or odds is included, and trading needs an authenticated signer this service deliberately does not hold, so no order can be placed here. When no provider is configured, or Polymarket could not be read, the answer is a 503 saying so, never an empty list — an empty list would claim there are no markets.","tags":["Markets"],"responses":{"200":{"description":"Markets as upstream reported them.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MarketSummary"}}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: no market provider is connected, so there is nothing to report. `UNAVAILABLE`: Polymarket could not be read just now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/markets/featured":{"get":{"operationId":"listFeaturedMarkets","summary":"The featured Polymarket events","description":"The events named in `POLYMARKET_FEATURED_EVENTS`, in that order, each re-read from Polymarket's Gamma API and held for five minutes. An event is shown only while Polymarket says it is open and before its end, judged when the answer is served, so a held read never outlives the event; one that has closed, ended or gone is listed in `dropped` instead. One that could not be read just now is listed in `unreadable`. No price or odds is included.","tags":["Markets"],"responses":{"200":{"description":"The featured events still open, and what was left out and why.","content":{"application/json":{"schema":{"type":"object","required":["data","dropped","unreadable","observedAt","disclosure"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PolymarketEvent"}},"dropped":{"type":"array","description":"Featured slugs not shown because the event is no longer open.","items":{"type":"object","required":["slug","reason"],"properties":{"slug":{"type":"string"},"reason":{"type":"string","enum":["GONE","CLOSED","ENDED","NO_OPEN_MARKET"]}}}},"unreadable":{"type":"array","items":{"type":"string"},"description":"Featured slugs Polymarket could not be read for just now. Not shown, and not claimed to be closed."},"observedAt":{"type":"string","format":"date-time","description":"When the oldest of the reads behind this answer was made."},"disclosure":{"type":"string","description":"The one sentence preCrime may say about Polymarket: it is not affiliated."}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED` without `POLYMARKET_GAMMA_API_URL`, or with no featured events chosen (`POLYMARKET_FEATURED_EVENTS`); `UNAVAILABLE` when none of them could be read. Never an empty list, which would read as no featured markets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/markets/related":{"get":{"operationId":"listRelatedMarkets","summary":"Polymarket events related to a term","description":"Polymarket events whose title names `q` as a whole word and which carry Polymarket's `crypto` tag, still open, at most ten, in Polymarket's order. Polymarket's own search is fuzzy — `PEPE` finds football assists — so its first twenty answers are only candidates here. An empty `data` with `complete: true` means no open crypto event names the term in its title; with `complete: false`, only that none of the first twenty does. Each term's search is held for a minute. No price or odds is included.","tags":["Markets"],"parameters":[{"name":"q","in":"query","required":true,"description":"A ticker, a name or a phrase: 2 to 60 characters with at least one letter or digit, once whitespace is collapsed.","schema":{"type":"string"}}],"responses":{"200":{"description":"The matching events.","content":{"application/json":{"schema":{"type":"object","required":["data","query","unreadable","complete","observedAt","disclosure"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PolymarketEvent"}},"query":{"type":"string","description":"The term as it was searched for and matched."},"unreadable":{"type":"integer","minimum":0,"description":"Events Polymarket returned that could not be read, so were not matched."},"complete":{"type":"boolean","description":"Whether Polymarket said its search had no candidates beyond those checked. False when it had more, or did not say: a match further down its list is then not shown."},"observedAt":{"type":"string","format":"date-time","description":"When Polymarket was searched for this term."},"disclosure":{"type":"string","description":"The one sentence preCrime may say about Polymarket: it is not affiliated."}}}}}},"400":{"description":"`INVALID_REQUEST`: `q` is missing, too short, too long or has no letter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED` without `POLYMARKET_GAMMA_API_URL`; `UNAVAILABLE` when Polymarket could not be read or this server is at its limit for searches (with `Retry-After`). Never an empty list, which would read as no related market.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/most-wanted":{"get":{"operationId":"listMostWanted","summary":"Targets ranked by case attention","description":"Attention only. The ordering is by how many active cases name an address and how much is staked on them — never by anything the address is alleged to have done — and the response carries that disclaimer so a consumer cannot strip the context by accident.","tags":["Aggregates"],"parameters":[{"name":"limit","in":"query","description":"Page size, 1 to 100. Defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"Ranked targets.","content":{"application/json":{"schema":{"type":"object","required":["data","disclaimer"],"properties":{"data":{"type":"array","items":{"type":"object","required":["address","chainId","activeCases","staked","cases"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"chainId":{"type":"integer"},"activeCases":{"type":"integer","description":"Open cases naming this address."},"staked":{"type":"array","description":"What is staked on this address's open cases, one entry per stake asset, from the pools' own totals. Assets are never added together.","items":{"type":"object","required":["asset","symbol","decimals","stakedBaseUnits","staked"],"properties":{"asset":{"type":"string","description":"The asset code."},"symbol":{"type":"string"},"decimals":{"type":"integer"},"stakedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"staked":{"$ref":"#/components/schemas/DecimalAmount"}}}},"cases":{"type":"array","description":"Up to three of the cases, so the row is readable without a second call.","items":{"type":"object","required":["id","title","caseTypeLabel"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"caseTypeLabel":{"type":"string"}}}}}}},"disclaimer":{"type":"string","description":"States that this is attention, not a finding of misconduct."}}}}}},"400":{"description":"`INVALID_REQUEST`: the limit failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/openapi.json":{"get":{"operationId":"getOpenApiDocument","summary":"This OpenAPI 3.1 document","description":"The machine-readable description of this API, with its path list derived from the same live route inventory `GET /v1/endpoints` reports — so the specification cannot describe a route that is not served, or omit one that is. No `servers` block is published: paths are relative to whatever host served this document, and guessing an origin from the process's bind address would be wrong behind any proxy. Anything the catalogue could not describe is listed on `x-precrime-documentation-gaps` rather than hidden.","tags":["Service"],"responses":{"200":{"description":"The document.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/payments/catalogue":{"get":{"operationId":"getPaymentCatalogue","summary":"What is for sale, at what price, and whether the rail can take the money","description":"Prices are the server's catalogue in US cents, paid at par in a dollar stablecoin. The accepted tokens and each family's split are read from the payment router, and the rail is checked on chain before anything is quoted: the router exists, its quote signer is this API's key, and every token is accepted at six decimals. `NOT_CONFIGURED` names the unset variables; `DEGRADED` names each failed check. A card processor is not connected, and says so.","tags":["Payments"],"responses":{"200":{"description":"The catalogue and the rail's state.","content":{"application/json":{"schema":{"type":"object","required":["status","reasons","chainId","router","tokens","splits","receivers","sponsorSlots","apiPlans","negotiated","launchPricing","cardProcessor"],"properties":{"status":{"type":"string","enum":["CONFIGURED","DEGRADED","NOT_CONFIGURED"]},"reasons":{"type":"array","items":{"type":"string"}},"chainId":{"type":["integer","null"]},"router":{"oneOf":[{"$ref":"#/components/schemas/EvmAddress"},{"type":"null"}]},"tokens":{"type":"array","items":{"$ref":"#/components/schemas/AcceptedToken"}},"splits":{"type":"object","required":["source","sponsor","api"],"properties":{"source":{"type":"string","enum":["CHAIN","UNAVAILABLE","NOT_CONFIGURED"]},"sponsor":{"oneOf":[{"$ref":"#/components/schemas/RevenueSplit"},{"type":"null"}]},"api":{"oneOf":[{"$ref":"#/components/schemas/RevenueSplit"},{"type":"null"}]}}},"receivers":{"type":["object","null"],"additionalProperties":true,"description":"The jar, staker and company receivers the router pays."},"checkedAt":{"allOf":[{"$ref":"#/components/schemas/Timestamp"}],"description":"When the rail was last checked."},"sponsorSlots":{"type":"array","items":{"type":"object","required":["slot","surface","pricing","unit","rateUsdCents","capacity","available","reason"],"properties":{"slot":{"type":"string"},"surface":{"type":"string"},"pricing":{"type":"string","enum":["PER_UNIT_TIME","PER_SEND"]},"unit":{"type":["string","null"],"description":"`TWELVE_HOURS`, `DAY` or `WEEK`; null for a slot priced per send."},"rateUsdCents":{"type":"string","pattern":"^\\d+$"},"capacity":{"type":"integer","description":"Sponsors the slot holds at once."},"available":{"type":"boolean","description":"False for a surface the product does not render yet."},"reason":{"type":["string","null"]}}}},"apiPlans":{"type":"array","items":{"type":"object","required":["tier","usdCents","validityDays","purchasable","reason"],"properties":{"tier":{"type":"string","enum":["BUILDER","RESEARCH","DESK"]},"usdCents":{"type":"string","pattern":"^\\d+$"},"validityDays":{"type":"integer"},"purchasable":{"type":"boolean"},"reason":{"type":["string","null"],"description":"Why a listed plan is not on sale, when it is not."}}}},"negotiated":{"type":"array","description":"Placements published as a band and priced per engagement, never here.","items":{"type":"object","required":["placement","fromUsdCents","toUsdCents"],"properties":{"placement":{"type":"string"},"fromUsdCents":{"type":"string","pattern":"^\\d+$"},"toUsdCents":{"type":"string","pattern":"^\\d+$"}}}},"launchPricing":{"type":"object","description":"Launch pricing (G-068, D-085): every sponsor slot block that starts within 60 days of the launch instant the server is configured with is half price, carried in the signed quote. `NOT_CONFIGURED` until that instant is set, and then every slot is at list price. The first three launch partners get one slot each for nothing, granted by an operator.","required":["status","launchAt","endsAt","discountBps","launchPartners"],"properties":{"status":{"type":"string","enum":["ACTIVE","ENDED","NOT_CONFIGURED"]},"launchAt":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"endsAt":{"oneOf":[{"$ref":"#/components/schemas/Timestamp"},{"type":"null"}]},"discountBps":{"type":["integer","null"]},"launchPartners":{"type":"object","required":["seats","granted"],"properties":{"seats":{"type":"integer","description":"How many free slots there are, ever."},"granted":{"type":"integer","description":"How many have been granted."}}}}},"cardProcessor":{"type":"string","const":"NOT_CONFIGURED"}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/points/{address}":{"get":{"operationId":"getPointsForAddress","summary":"One address's live points this season","description":"The address's row in the latest table, with its place in each source and, once the weights are revealed, its total and overall rank. An address with nothing this season answers 200 with `entry: null`, which is a statement about the season, not a missing resource. The same season fields, basis, units and notes as the leaderboard.","tags":["Points"],"parameters":[{"name":"address","in":"path","required":true,"description":"An EVM address.","schema":{"type":"string"}},{"name":"season","in":"query","description":"The season's number. Defaults to the newest computed.","schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The season and the address's row, or null.","content":{"application/json":{"schema":{"type":"object","required":["season","basis","computedAt","account","entry"],"properties":{"season":{"type":"object"},"basis":{"type":"string","enum":["MEASURES","POINTS"]},"units":{"type":"object","additionalProperties":{"type":"string"}},"computedAt":{"type":"string","format":"date-time"},"notes":{"type":"array","items":{"type":"string"}},"account":{"$ref":"#/components/schemas/EvmAddress"},"entry":{"oneOf":[{"$ref":"#/components/schemas/PointsEntry"},{"type":"null"}]}}}}}},"400":{"description":"`INVALID_REQUEST`: not an address, or not a season number.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: that season has no live table here.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: no season's live points were ever computed here.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/points/leaderboard":{"get":{"operationId":"getPointsLeaderboard","summary":"The season's live points, ranked by one source or by total","description":"The worker's latest hourly table for a season (the newest season when `season` is omitted), read from rows: this route never touches the chain. `basis` says what the figures count. While the season's weights are sealed it is MEASURES: each source in its own unit — dollars of fee borne for staking, creating and referring, PRECRIME-days for holding, upheld challenges, dollar-days in the backer vault — ranked within that source, with no total, because a total needs the weights; `source=total` is refused then. Once the weights are revealed it is POINTS, with a total and an overall rank. `notes` say what the run could not count yet. Every figure is a decimal string with six places. What the season pays is decided by its build, not by this table. 503 NOT_CONFIGURED when no season was ever computed here; 404 for a season with no table.","tags":["Points"],"parameters":[{"name":"season","in":"query","description":"The season's number: 0 for Genesis. Defaults to the newest computed.","schema":{"type":"integer","minimum":0}},{"name":"source","in":"query","description":"What to rank by: `total`, or one source. Defaults to `total` once the weights are revealed and `staking` before.","schema":{"type":"string","enum":["total","staking","creator","referral","holding","quality","backing"]}},{"name":"limit","in":"query","description":"Page size, 1 to 100. Defaults to 50.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"offset","in":"query","description":"Rows to skip, for the next page.","schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The season, its basis and units, and one page of the ranking.","content":{"application/json":{"schema":{"type":"object","required":["season","basis","computedAt","notes","source","entries","final"],"properties":{"season":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"startsAt":{"type":"string","format":"date-time"},"endsAt":{"type":"string","format":"date-time"}}},"basis":{"type":"string","enum":["MEASURES","POINTS"]},"units":{"type":"object","description":"What each source's figure counts, in words.","additionalProperties":{"type":"string"}},"computedAt":{"type":"string","format":"date-time"},"rowsHash":{"type":"string","description":"keccak256 of the rows the run scored."},"accounts":{"type":"integer","description":"Addresses in the whole table."},"notes":{"type":"array","items":{"type":"string"}},"final":{"type":"boolean","const":false},"finalNote":{"type":"string"},"source":{"type":"string"},"ranked":{"type":"integer","description":"Addresses ranked by this source."},"offset":{"type":"integer"},"seasons":{"type":"array","items":{"type":"integer"}},"entries":{"type":"array","items":{"$ref":"#/components/schemas/PointsEntry"}}}}}}},"400":{"description":"`INVALID_REQUEST`, including `source=total` while the weights are sealed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: that season has no live table here.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: no season's live points were ever computed here.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/prices":{"get":{"operationId":"listPrices","summary":"The price ticker: the registry's Chainlink feeds, as last read","description":"Each feed the venue registry offers for a price case or a Stock Token case, with its latest answer, round, age and status; for a Stock Token also the token's multiplier, staged multiplier and pause, and the market's state under the current calendar version; for a crypto feed, Hyperliquid's perp beside it as context (`hyperliquid`), which never settles anything. Read through Multicall3 at one block, at most once a minute and only while something asks, and held a minute. `NOT_CONFIGURED` without an RPC; `UNAVAILABLE` when the read failed, or for one feed when its call failed or its answer is older than its heartbeat (counted in open-market time for an equity feed). A figure it did not read is never shown. For reading only: a price case settles from the resolver's own reads at finalised blocks.","tags":["Prices"],"parameters":[{"name":"feed","in":"query","description":"One feed by its registry key (`BTC_USD`, `EQ_TSLA_USD`). Omitted, every feed.","schema":{"type":"string"}}],"responses":{"200":{"description":"The ticker.","content":{"application/json":{"schema":{"type":"object","required":["status","detail","readAt","blockNumber","feeds","note"],"properties":{"status":{"type":"string","enum":["OK","UNAVAILABLE","NOT_CONFIGURED"]},"detail":{"type":["string","null"]},"readAt":{"type":["string","null"]},"blockNumber":{"type":["string","null"]},"feeds":{"type":"array","items":{"$ref":"#/components/schemas/FeedPrice"}},"note":{"type":"string"}}}}}},"400":{"description":"`INVALID_REQUEST`: `feed` is not a registry key's shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`FEED_NOT_LISTED`: the ticker reads no feed by that key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/private/api-keys":{"get":{"operationId":"listApiKeys","summary":"List live API keys","description":"The caller's own un-revoked keys with their quota consumption. Secrets are absent because they are not stored, only hashed — a list that could show them would mean the store could leak them.","tags":["API keys"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"Live keys.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeySummary"}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"keys:read","x-precrime-described":true},"post":{"operationId":"createApiKey","summary":"Mint a scoped API key","description":"Session-only. A key able to mint keys would let a leaked read-only key escalate itself, and revoking the original would then not contain the compromise. Requested scopes are sanitised rather than rejected: unknown and session-only scopes are dropped, and the response lists what was actually granted. The secret is returned exactly once — only its hash is retained, so it cannot be recovered later. The daily quota is not a request field: it comes from the account's plan, so a caller cannot grant itself a larger allowance than it pays for.","tags":["API keys"],"security":[{"sessionBearer":[]}],"requestBody":{"description":"The key's name and the scopes asked for.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"A label, 1 to 100 characters."},"scopes":{"type":"array","items":{"type":"string"},"description":"Up to 25 scopes. Unknown and session-only ones are dropped, not refused."}}}}}},"responses":{"201":{"description":"The key, with its secret, for the only time.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiKeySummary"}],"type":"object","required":["token"],"properties":{"token":{"type":"string","description":"The secret, prefixed `pc_`. Shown once and never again. Store it now or mint another."}}}}}},"400":{"description":"`INVALID_REQUEST`: the name or scope list failed validation. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the credential is missing the `keys:write` scope, named in `requiredScope`; or `PLAN_HAS_NO_API_ACCESS` when the account's plan is configured with an allowance of zero.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`QUOTA_NOT_CONFIGURED`: no daily allowance is set for the account's plan, so no key can be minted. Reported as a configuration fault: a key minted with a guessed limit would invent the figure the plan exists to set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"keys:write","x-precrime-described":true}},"/v1/private/api-keys/{id}":{"delete":{"operationId":"revokeApiKey","summary":"Revoke an API key","description":"Session-only, for the same reason minting is. Revocation is a timestamp rather than a deletion, so the record of what the key could do and when it was withdrawn survives.","tags":["API keys"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The key id from the list read.","schema":{"type":"string"}}],"responses":{"204":{"description":"Done. No body, because there is nothing to say that the status has not said."},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"keys:write","x-precrime-described":true}},"/v1/private/auth/logout":{"post":{"operationId":"logout","summary":"Revoke the current session","description":"Session-only, and not merely by convention: an API key presenting itself here is refused, because a key has no session to end and letting one revoke a person's session would be a denial-of-service primitive handed to a token in a CI secret.","tags":["Identity"],"security":[{"sessionBearer":[]}],"responses":{"204":{"description":"Done. No body, because there is nothing to say that the status has not said."},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"sessions:write","x-precrime-described":true}},"/v1/private/backers/me":{"get":{"operationId":"getMyBackerPosition","summary":"The caller's place in the backer vault, per verified wallet","description":"One row per verified wallet, read at one block. A request whose epoch has already rolled is reported as what it became — shares, or claimable dollars — exactly as the contract's `settle()` would credit it on the wallet's next call, rather than as money still queued with a cancel that would revert. `valueEstimateBaseUnits` prices shares at the vault's `value()` over its shares, with open positions at cost: an estimate until the next roll fixes the price. `reserve` is the wallet's part of the opted-in instant reserve (G-003), settled the same way; a rolled reserve refund or exit is already in `claimableBaseUnits`. 503 NOT_CONFIGURED when no vault address is set, UNAVAILABLE when it could not be read.","tags":["Backers"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"The vault's epoch and value, and each wallet's shares, requests and claimable.","content":{"application/json":{"schema":{"type":"object","required":["vault","wallets"],"properties":{"vault":{"type":"object","required":["address","epoch","epochEnd","valueIsEstimate","blockNumber"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"asset":{"$ref":"#/components/schemas/EvmAddress"},"assetSymbol":{"type":["string","null"]},"assetDecimals":{"type":["integer","null"]},"epoch":{"type":"integer"},"epochEnd":{"type":"string","format":"date-time"},"valueBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"totalSharesBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"valueIsEstimate":{"type":"boolean"},"blockNumber":{"type":"string"}}},"wallets":{"type":"array","items":{"type":"object","required":["address","sharesBaseUnits","valueEstimateBaseUnits","pendingDeposit","pendingWithdrawal","claimableBaseUnits","reserve"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"sharesBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"valueEstimateBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"pendingDeposit":{"type":["object","null"],"description":"Dollars queued this epoch. Cancellable until the roll.","properties":{"epoch":{"type":"integer"},"amountBaseUnits":{"$ref":"#/components/schemas/BaseUnits"}}},"pendingWithdrawal":{"type":["object","null"],"description":"Shares queued this epoch, paid at the next roll's price.","properties":{"epoch":{"type":"integer"},"sharesBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"valueEstimateBaseUnits":{"$ref":"#/components/schemas/BaseUnits"}}},"claimableBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"What `withdraw()` pays this wallet now."},"reserve":{"type":"object","required":["shareUnits","valueEstimateBaseUnits","pendingDeposit","pendingWithdrawal"],"description":"The wallet's instant reserve shares, valued at the price the next roll would use, and its reserve requests still queued this epoch.","properties":{"shareUnits":{"type":"string","pattern":"^\\d+$"},"valueEstimateBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"pendingDeposit":{"type":["object","null"],"description":"The reserve part of opted-in deposits queued this epoch. Cancelled with the plain part.","properties":{"epoch":{"type":"integer"},"amountBaseUnits":{"$ref":"#/components/schemas/BaseUnits"}}},"pendingWithdrawal":{"type":["object","null"],"description":"Reserve shares queued this epoch to leave at the next roll.","properties":{"epoch":{"type":"integer"},"shareUnits":{"type":"string","pattern":"^\\d+$"},"valueEstimateBaseUnits":{"$ref":"#/components/schemas/BaseUnits"}}}}}}}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: no vault address is set. `UNAVAILABLE`: the vault could not be read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"account:read","x-precrime-described":true}},"/v1/private/cases":{"post":{"operationId":"createCaseDraft","summary":"Create a draft case","description":"Creates an off-chain draft and returns what the client needs to construct the on-chain `createCase` transaction. `acknowledgedPolicy` must be literally true: the product's safety position depends on cases being read as time-bounded forecasts about public data rather than accusations, and that has to be confirmed before one is published, not after. The rule and timings are checked exactly as `POST /v1/private/cases/preview` checks them, and then the rule is checked against what this deployment can settle (decision D-035): a case type that needs a feed the operator has not configured, a venue nobody has verified, or an evaluator this build does not have answers 422 `NOT_RESOLVABLE` with the reason. `GET /v1/case-templates` reports the same answer per type before anything is typed.","tags":["Cases"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"requestBody":{"description":"The proposed case, plus the policy acknowledgement.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","rule","stakeCutoffAt","resolutionDeadlineAt","challengeWindowSeconds","acknowledgedPolicy"],"properties":{"title":{"type":"string","description":"4 to 140 characters."},"rule":{"$ref":"#/components/schemas/CaseRule"},"stakeCutoffAt":{"$ref":"#/components/schemas/Timestamp"},"resolutionDeadlineAt":{"$ref":"#/components/schemas/Timestamp"},"challengeWindowSeconds":{"type":"integer","description":"Challenge window length."},"acknowledgedPolicy":{"type":"boolean","const":true,"description":"Must be true. Not a default, and not inferable from anything else."},"stakeAsset":{"type":["string","null"],"pattern":"^0x[0-9a-fA-F]{40}$","description":"The stake asset, by address, from `GET /v1/case-factory`. Omitted or null leaves the choice to the deploy step. When a factory is configured the asset must be one it allows; publishing later refuses a pool built in any other."}}}}}},"responses":{"201":{"description":"The draft, with what the publishing transaction needs.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Case"}],"type":"object","required":["publishWith"],"properties":{"publishWith":{"type":"object","required":["ruleHash","chainId"],"description":"The commitment and chain the `createCase` transaction must use.","properties":{"ruleHash":{"$ref":"#/components/schemas/Hash32"},"chainId":{"type":"integer"}}}}}}}},"400":{"description":"`INVALID_REQUEST`: the body failed the request schema, including an acknowledgement that is anything but literally `true`. `WINDOW_*`: the rule's window does not fit the case's times, exactly as `POST /v1/private/cases/preview` refuses it (decision D-067). `DEADLINE_TOO_FAR`: the resolution deadline is further from now than the factory's `maxCaseDuration()`, read from the chain, lets a case run (decision D-071); checked at draft only, since preview reads no chain. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`STAKE_ASSET_NOT_ALLOWED`: the factory does not accept the chosen stake asset. Nothing is stored; choose one it lists, or leave the choice to the deploy step.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`NOT_RESOLVABLE`: this deployment cannot settle the rule. The message names the missing environment variable, venue or evaluator.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`CHAIN_UNAVAILABLE` or `CHAIN_MISMATCH`: a factory is configured and could not be read to check the stake asset, the case-duration cap and the challenge-window floor against it. Nothing is stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"cases:write","x-precrime-described":true}},"/v1/private/cases/{id}":{"get":{"operationId":"getMyCase","summary":"One of the caller's own cases","description":"Drafts included, which the public read answers 404 for. Another account's case, or one that does not exist, is the same 404, so this cannot be used to learn which drafts exist.","tags":["Cases"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The case's public id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The case, in any status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Case"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"cases:read","x-precrime-described":true}},"/v1/private/cases/{id}/evidence":{"get":{"operationId":"listCaseEvidenceAsCaller","summary":"The same record, read as the signed-in caller","description":"A separate path rather than a header-sniffing branch on the public one, because only `/v1/private` is authenticated. The difference in the answer is exactly the exceptions the access rules grant: the caller's own submissions, the case creator's, a dispute party's, and an operator reviewer's. An API key carries its user's own exceptions but never the reviewer one, because a key holds no operator permission.","tags":["Evidence"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The public case id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The record, as this reader may see it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvidenceListing"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"evidence:read","x-precrime-described":true},"post":{"operationId":"submitEvidenceReference","summary":"Submit a reference to material published elsewhere","description":"Any kind may be submitted, settlement-grade ones included, and the receipt is where the difference is made plain: a submitted `CHAIN_LOG` is recorded as a settlement-grade kind and is not admitted, because nothing here read the chain. The store is content-addressed by case and hash, so the same capture submitted twice is one item and the second submitter gets the existing item back as a 409, not a 201 claiming they added something. The submitter's claimed times are hashed into the body under a key naming whose claim they are, and never written to the item's `occurredAt`. `acknowledgedNotAVerdict` must be literally true, for the same reason a case draft demands its acknowledgement. The case must still be accumulating evidence: a settled, voided or cancelled one is closed, and so is one whose status this build cannot read.","tags":["Evidence"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The public case id.","schema":{"type":"string"}}],"requestBody":{"description":"The kind, where the material is or what the submitter wrote, and the acknowledgement.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["kind","acknowledgedNotAVerdict"],"properties":{"kind":{"allOf":[{"$ref":"#/components/schemas/EvidenceKind"}],"description":"A kind this build recognises; anything else is refused, not stored."},"uri":{"type":"string","format":"uri","description":"Where the material is, up to 2048 characters. Only `http` and `https`: a `data:` or `javascript:` URI rendered in a dossier is a delivery mechanism, and a scheme nobody can fetch is not a pointer anybody can check. Optional when `text` carries the whole submission, as a written note does; one of the two is required."},"text":{"type":"string","description":"Up to 2000 characters, hashed with everything else: two submissions whose text differs are two items. A report that a verdict is wrong goes to `/v1/private/cases/{id}/report-verdict` instead, which also pages the operators."},"claimedObservedAt":{"allOf":[{"$ref":"#/components/schemas/Timestamp"}],"description":"When the submitter says they looked. Recorded as a claim."},"claimedOccurredAt":{"allOf":[{"$ref":"#/components/schemas/Timestamp"}],"description":"When the submitter says the event happened. Also a claim."},"acknowledgedNotAVerdict":{"type":"boolean","const":true}}}}}},"responses":{"201":{"description":"Stored, and here is what it was accepted as.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionReceipt"}}}},"400":{"description":"`INVALID_REQUEST` when the body failed the request schema, or `UNRECOGNISED_EVIDENCE_KIND` with a `detail` when the kind is not one this build knows — refused rather than stored as an opaque string, because a row whose admissibility and retention are both unknown can neither be published nor expired. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Two refusals share this status and the body says which. A `SubmissionReceipt` with `alreadySubmitted: true` means the same content was already on this case's record and the existing item is returned. An error envelope with `CASE_CLOSED_TO_SUBMISSIONS` or `UNRECOGNISED_CASE_STATUS` and a `detail` means the case cannot accept submissions at all and nothing was stored. Branch on the presence of `error`.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/SubmissionReceipt"},{"$ref":"#/components/schemas/Error"}]}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"evidence:write","x-precrime-described":true}},"/v1/private/cases/{id}/evidence/file":{"post":{"operationId":"submitEvidenceFile","summary":"Submit a file","description":"The bytes travel base64-encoded inside a JSON body rather than as a typed binary body, on purpose: a `Content-Type` header is matched by a parser before the handler runs, which would let the sender's claim about the bytes decide whether the bytes are ever looked at. The type is sniffed from the bytes — PNG and JPEG up to 8 MiB, PDF up to 16 MiB — metadata is stripped or the file is refused, and the hash is taken over the bytes that are retained, not the ones that arrived. Every upload is `USER_UPLOAD` and therefore context, whatever it depicts: a screenshot of a block explorer is a picture of a claim the chain can be re-read for. No current deployment has an evidence object store, so `bytes` reports `NOT_CONFIGURED`: the hash and metadata are kept, the bytes are not. The route's own body limit is the encoded size of a 16 MiB file plus 4 KiB; a larger body is refused with a 413 `BODY_TOO_LARGE` before it is parsed.","tags":["Evidence"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The public case id.","schema":{"type":"string"}}],"requestBody":{"description":"The file, and what the sender claims about it.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["contentBase64","acknowledgedNotAVerdict"],"properties":{"contentBase64":{"type":"string","description":"Standard base64, no whitespace, not the URL-safe alphabet. The decoded size is computed from the encoded length before anything is decoded, so an oversized file is refused without being allocated."},"declaredContentType":{"type":"string","description":"Up to 200 characters. Recorded for the audit trail; decides nothing."},"declaredFilename":{"type":"string","description":"Up to 300 characters. Also a claim."},"acknowledgedNotAVerdict":{"type":"boolean","const":true}}}}}},"responses":{"201":{"description":"Stored, and here is what it was accepted as.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionReceipt"}}}},"400":{"description":"`INVALID_REQUEST` when the body failed the request schema, or `MALFORMED_BASE64` with a `detail` when no file could be recovered from the content field — which is a statement about the encoding, not about the file. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Two refusals share this status and the body says which. A `SubmissionReceipt` with `alreadySubmitted: true` means byte-identical content was already on this case's record. An error envelope with `CASE_CLOSED_TO_SUBMISSIONS` or `UNRECOGNISED_CASE_STATUS` and a `detail` means the case cannot accept submissions. Branch on the presence of `error`.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/SubmissionReceipt"},{"$ref":"#/components/schemas/Error"}]}}}},"413":{"description":"`BODY_TOO_LARGE` when the body is over the route's limit and was never read, `TOO_LARGE` when the file exceeds 16 MiB, or `TOO_LARGE_FOR_TYPE` when it exceeds the limit for what it turned out to be. Send less.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`DENIED_CONTENT` for a type that is never accepted, or `UNRECOGNISED_CONTENT` when the bytes match no accepted type. Send something else. `UNSUPPORTED_MEDIA_TYPE` when the request itself was not sent as `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`EMPTY_FILE`, `MALFORMED_OR_TRUNCATED`, or `LOCATION_METADATA_PRESENT` for a PDF carrying coordinates that cannot be stripped here. The format is accepted; this file is broken or needs re-exporting.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"evidence:write","x-precrime-described":true}},"/v1/private/cases/{id}/publish":{"post":{"operationId":"publishCase","summary":"Attach a deployed pool to a draft","description":"Called after the `createCase` transaction confirms. Only the pool and the transaction are taken from the caller; everything else is read from the chain before the case goes live. The preCrime factory must have created the pool, the pool's rule hash must equal the draft's, so must its stake cutoff and resolution deadline, its creator must be one of the caller's verified wallets, and the transaction must be the one that created it. The bond and tier recorded are the chain's, not the caller's, and so is the pool's review timeout (`reviewTimeout()`, G-002), which dates every later challenge's deadline. Re-publishing the pool already attached is idempotent and answers 200 again.","tags":["Cases"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The public id of the draft being published.","schema":{"type":"string"}}],"requestBody":{"description":"The deployed pool and the transaction that created it.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["poolAddress","createTxHash"],"properties":{"poolAddress":{"$ref":"#/components/schemas/EvmAddress"},"createTxHash":{"$ref":"#/components/schemas/Hash32"}}}}}},"responses":{"200":{"description":"The published case.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Case"}}}},"400":{"description":"`INVALID_REQUEST`: the body failed the request schema. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing the `cases:write` scope, named in `requiredScope`, or the caller did not create this draft.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The pool cannot be attached: `POOL_NOT_FROM_FACTORY` (the preCrime factory did not create it), `RULE_HASH_MISMATCH` (it commits to a different rule than the draft), `STAKE_ASSET_MISMATCH` (it is staked in a different asset than the draft chose), `TIMING_MISMATCH` (its stake cutoff or resolution deadline, to the second, is not the draft's, which the rule's window was checked against — decision D-067 — or its challenge window is not the one the case page shows), `STAKE_ASSET_NOT_SUPPORTED` (its asset reports a symbol, decimals or burn route the product cannot display), `CREATOR_NOT_VERIFIED` (a wallet the caller has not verified created it), `TX_DOES_NOT_CREATE_POOL` (the transaction did not create it, or is not mined), `SETTLER_REQUIRED` (a crypto or dollar price case whose pool names no settler, D-086), `SETTLER_REFUSED` (any other case whose pool names one), `TERMS_MISMATCH` (the pool's settler, or a term it bound — feed, decimals, direction, measure, move or level, window end, gap — is not the rule's), `INVALID_CONFIRM_TIMEOUT` (a confirmation timeout no factory builds a pool with), `CHAIN_MISMATCH` (the draft is on a chain the API does not read), or `ALREADY_PUBLISHED` (the draft is live against a different pool).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`CHAIN_NOT_CONFIGURED` when no case factory or RPC endpoint is set, so no pool can be checked; `CHAIN_UNAVAILABLE` when the chain could not be read. Nothing is published.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"cases:write","x-precrime-described":true}},"/v1/private/cases/{id}/report-verdict":{"post":{"operationId":"reportWrongVerdict","summary":"Report a wrong verdict","description":"Third-party challenges are retired (D-086): anyone who sees a wrong verdict reports it here. The report is filed as a public `COMMUNITY_NOTE`, through the same envelope as any note, and pages the operators once, who may escalate it on chain while the verdict is not yet due. A page is a message, never a decision: only the operator's signed escalation moves the pool. The same words twice are one item and page nobody again.","tags":["Evidence"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The public case id.","schema":{"type":"string"}}],"requestBody":{"description":"What is wrong with the verdict, and the acknowledgement.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["text","acknowledgedNotAVerdict"],"properties":{"text":{"type":"string","description":"10 to 2000 characters, trimmed."},"acknowledgedNotAVerdict":{"type":"boolean","const":true}}}}}},"responses":{"201":{"description":"Filed, and who was paged.","content":{"application/json":{"schema":{"type":"object","required":["evidenceId","caseId","contentHash","operatorsPaged","escalateBy"],"properties":{"evidenceId":{"type":"string"},"caseId":{"type":"string"},"alreadySubmitted":{"type":"boolean"},"contentHash":{"type":"string"},"access":{"type":"object","additionalProperties":true},"operatorsPaged":{"type":"integer","description":"Messages queued to operators; 0 with `unsent` when none could be."},"unsent":{"type":["string","null"],"description":"`NO_OPERATOR_CHAT` when nobody could be paged; null otherwise."},"escalateBy":{"type":["string","null"],"format":"date-time","description":"When escalation closes for this verdict; null in review or unknown."},"verification":{"type":"object","additionalProperties":true}}}}}},"400":{"description":"`INVALID_REQUEST`: the text is under 10 or over 2000 characters. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`NO_VERDICT_TO_REPORT`: the case has no provisional verdict. The case closed to submissions answers `CASE_CLOSED_TO_SUBMISSIONS`; a repeat answers with the item.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"evidence:write","x-precrime-described":true}},"/v1/private/cases/mine":{"get":{"operationId":"listMyCases","summary":"The caller's own cases","description":"Includes drafts, which no public read returns. Capped at the most recent 100 and not paginated: this is a creator's own working set, not the public record.","tags":["Cases"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"The caller's cases, newest first.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Case"}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"cases:read","x-precrime-described":true}},"/v1/private/cases/preview":{"post":{"operationId":"previewCase","summary":"Validate a proposed case and show what publishing would commit","description":"Writes nothing. Returns the rule hash the pool would be constructed with and the narrative the case would carry, so a creator can read the exact commitment before making one. The proposal is checked twice: the request schema checks shape and answers 400, then the domain layer checks that the rule is a valid template instance and that the timings are coherent — staking open for at least 15 minutes (`STAKE_WINDOW_TOO_SHORT`), the deadline after the cutoff (`DEADLINE_BEFORE_CUTOFF`), the challenge window between the case factory's floor (`minChallengeWindow()`, read from the chain: an hour at launch, a timelock setting down to 15 minutes, decision D-083) and 72 hours (`CHALLENGE_WINDOW_OUT_OF_RANGE`) — and that the rule's window fits the case (decision D-067): a new case states one (`WINDOW_REQUIRED`), counts only what happens after staking closes (`WINDOW_NOT_STAKE_FIRST`), ends after the cutoff (`WINDOW_BEFORE_CUTOFF`) by at least 15 minutes, or a price rule's averaging period (`WINDOW_TOO_SHORT`), and leaves the resolver 24 hours before the deadline (`WINDOW_PAST_DEADLINE`). A lightning round (decision D-084) is read from the times themselves, never from a flag: staking of at most 30 minutes from now, counting of at most 30 and a challenge window of at most an hour. Such a draft that keeps to the table and the preset — a type, venue and feed `GET /v1/case-templates` offers lightning on, counting one of its choices, the challenge window the factory's floor — takes a 2-hour deadline instead of 24 (`lightningDraft: true`); one that asks for less than 24 hours without keeping to them answers `LIGHTNING_NOT_OFFERED` or `LIGHTNING_PRESET` with the reason. Those refusals answer 400 with their code, and so does a rule the rule schema itself refuses (`INVALID_CASE_RULE`, whose message names the field), a version 4 rule included: this path takes versions 2 and 3.","tags":["Cases"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"requestBody":{"description":"The proposed title, rule and timings.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","rule","stakeCutoffAt","resolutionDeadlineAt","challengeWindowSeconds"],"properties":{"title":{"type":"string","description":"4 to 140 characters."},"rule":{"$ref":"#/components/schemas/CaseRule"},"stakeCutoffAt":{"$ref":"#/components/schemas/Timestamp"},"resolutionDeadlineAt":{"$ref":"#/components/schemas/Timestamp"},"challengeWindowSeconds":{"type":"integer","description":"How long a provisional verdict may be challenged."}}}}}},"responses":{"200":{"description":"What publishing would commit.","content":{"application/json":{"schema":{"type":"object","required":["ruleHash","rule","condition","meaning","source","lightningDraft"],"properties":{"ruleHash":{"$ref":"#/components/schemas/Hash32"},"rule":{"$ref":"#/components/schemas/CaseRule"},"lightningDraft":{"type":"boolean","description":"Whether these times are a lightning round: they keep to the lightning preset and take its deadline, under a day after counting stops (decision D-084). The same times kept with a day's deadline or more are a standard case. The case itself is flagged only at publish, from its pool's own times, and the flag says what this said."},"condition":{"type":"string","description":"The resolvable condition in one sentence."},"meaning":{"type":"string","description":"What a GUILTY verdict would and would not say."},"source":{"type":"string","description":"Which data would settle it, and from what finality."}}}}}},"400":{"description":"`INVALID_REQUEST`: the body failed the request schema — a missing field, a title outside 4 to 140 characters, a timestamp that does not parse — and the issues name the field. `INVALID_CASE_RULE`: the rule is not a valid template instance; the message names the field. Or one of the timing and window codes above. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`CHAIN_UNAVAILABLE` or `CHAIN_MISMATCH`: a factory is configured and could not be read for its challenge-window floor, so the window cannot be checked against it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"cases:write","x-precrime-described":true}},"/v1/private/entitlement":{"get":{"operationId":"getEntitlement","summary":"What this account holds, and why","description":"Answers for the caller's own account only. The tier is the stronger of the subscription route and the lock route, never their sum, and every lock found at the account's verified wallets is listed — including the ones that did not qualify, with the reason each was refused. `lockIndex` travels with them because an empty list under a CURRENT index means there is no lock, and the same empty list under NOT_STARTED means nobody has looked. The day's usage is read, never charged: a dashboard left open must not spend the allowance it is displaying.","tags":["Access"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"The account's tier, its locks and its allowance.","content":{"application/json":{"schema":{"type":"object","required":["tier","route","state","api","locks","lockIndex"],"properties":{"tier":{"type":"string","enum":["EXPLORER","BUILDER","RESEARCH","DESK"]},"route":{"type":"string","enum":["SUBSCRIPTION","TOKEN_LOCK","DEFAULT"]},"state":{"type":"string","enum":["ACTIVE","GRACE","DEFAULT"]},"expiresAt":{"type":["string","null"],"format":"date-time"},"downgradesTo":{"type":"string"},"features":{"type":"array","items":{"type":"string","enum":["LIVE_ALERTS","WEBHOOKS"]}},"api":{"type":"object","additionalProperties":true},"locks":{"type":"array","items":{"type":"object","additionalProperties":true}},"lockIndex":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["NOT_CONFIGURED","NOT_STARTED","STALE","CURRENT"]},"lagBlocks":{"type":["integer","null"]}}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"account:read","x-precrime-described":true}},"/v1/private/grants/bond":{"post":{"operationId":"requestBondGrantVoucher","summary":"A voucher to claim the first-bond grant from a wallet","description":"Signed for a wallet verified on the caller's account, when the account has a draft case and has published none: the grant is for a first case. The grant contract is read first — paused, none left, this wallet or this account already granted — so a voucher is never signed for a claim that would revert. The wallet sends `claim(account, deadline, signature)` to the contract itself; the voucher expires after an hour.","tags":["Cases"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"requestBody":{"description":"The wallet that will claim.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["wallet"],"properties":{"wallet":{"$ref":"#/components/schemas/EvmAddress"}}}}}},"responses":{"200":{"description":"The voucher.","content":{"application/json":{"schema":{"type":"object","required":["contract","wallet","account","deadline","amountBaseUnits","signature"],"properties":{"contract":{"$ref":"#/components/schemas/EvmAddress"},"wallet":{"$ref":"#/components/schemas/EvmAddress"},"account":{"type":"string","pattern":"^0x[0-9a-f]{64}$","description":"The account's bytes32 key, the same for every voucher it is given."},"deadline":{"type":"string","pattern":"^\\d+$","description":"Unix seconds."},"amountBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"signature":{"type":"string","pattern":"^0x[0-9a-fA-F]+$"}}}}}},"400":{"description":"`INVALID_REQUEST`: the body is not JSON, is empty under a JSON content type, or failed the request schema; the message or the issues say which.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`WALLET_NOT_VERIFIED`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`NOT_A_FIRST_CASE`, `NO_DRAFT`, `PAUSED`, `NONE_LEFT`, `WALLET_ALREADY_GRANTED` or `ACCOUNT_ALREADY_GRANTED`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`, `SIGNER_NOT_TRUSTED` or `UNAVAILABLE`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"cases:write","x-precrime-described":true}},"/v1/private/hyperliquid/precheck":{"post":{"operationId":"precheckAccountMarket","summary":"An account market's live preview and draft checks","description":"Reads the Hyperliquid account (its role, its positions, the pace of its latest fills and the addresses Hyperliquid links to it), held 15 seconds per account, and runs the draft checks of decision D-082 in order: `NOT_RESOLVABLE` (naming `HYPERLIQUID_INFO_URL` or `HL_ACCOUNT_MARKETS_ENABLED`), `RECORDER_UNAVAILABLE`, `ACCOUNT_NOT_TRADING`, `COIN_NOT_LISTED`, `POSITION_NOT_HELD`, `ACCOUNT_MARKETS_FULL`, `ACCOUNT_TOO_ACTIVE`. The first that applies is `refusal`; the preview is returned either way. `linkedToCaller` says whether one of the caller's verified wallets is the account or linked to it, decided here: such a case opens, and is labelled as opened by the account's owner once published.","tags":["Cases"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"requestBody":{"description":"The account, the coin and the side.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["account","coin","side"],"properties":{"account":{"$ref":"#/components/schemas/EvmAddress"},"coin":{"type":"string","description":"A main-dex perp name, case kept."},"side":{"type":"string","enum":["LONG","SHORT"]}}}}}},"responses":{"200":{"description":"The preview and the checks.","content":{"application/json":{"schema":{"type":"object","required":["ok","refusal","preview","linkedToCaller","recorders"],"properties":{"ok":{"type":"boolean"},"refusal":{"type":["object","null"],"description":"`{code, status, message}`: the first check that refused, by name.","additionalProperties":true},"role":{"type":["string","null"]},"position":{"type":["object","null"],"additionalProperties":true},"positions":{"type":"array","items":{"type":"object","additionalProperties":true}},"coins":{"type":"array","items":{"type":"string"},"description":"The live main-dex perps, for the coin select; empty when unread."},"pace":{"type":["object","null"],"additionalProperties":true},"preview":{"type":["string","null"]},"linkedToCaller":{"type":["object","null"],"additionalProperties":true},"ownerNotice":{"type":["string","null"]},"recorders":{"type":"array","items":{"type":"object","additionalProperties":true}},"readAt":{"type":["string","null"]}}}}}},"400":{"description":"`INVALID_REQUEST`: the account, coin or side is malformed. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`HL_BUDGET_SPENT` with `Retry-After`, or `UNAVAILABLE`: Hyperliquid could not be read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"cases:write","x-precrime-described":true}},"/v1/private/me":{"get":{"operationId":"getMe","summary":"Who this credential is","description":"Reports the credential kind as well as the user, because the two are not interchangeable: a session acts as the person and holds every delegatable scope, while a key holds only what it was granted. `scopes` is the literal string `\"all\"` for a session and an array for a key, so a client cannot mistake one for the other.","tags":["Identity"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"The resolved principal.","content":{"application/json":{"schema":{"type":"object","required":["authenticated","credential","userId","walletAddress","scopes"],"properties":{"authenticated":{"type":"boolean","description":"Always true; a failure is a 401."},"credential":{"type":"string","enum":["SESSION","API_KEY"],"description":"Which credential kind resolved."},"userId":{"type":"string","description":"The owning account."},"walletAddress":{"type":["string","null"],"description":"The session's wallet. Null for a key, which carries no wallet, and null for a session whose wallet record could not be read."},"scopes":{"oneOf":[{"type":"string","enum":["all"]},{"type":"array","items":{"type":"string"}}],"description":"`\"all\"` for a session, or the key's granted scopes."}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/private/moderation/cases/{caseId}":{"get":{"operationId":"getModerationCase","summary":"One case, with what removing it would do","description":"The case as it stands, and the effect a removal would have — or, after a removal, did have. Served separately from the queue so an operator can read the consequence before acting rather than after.","tags":["Moderation"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"caseId","in":"path","required":true,"description":"The case's internal id, as the queue reports it.","schema":{"type":"string"}}],"responses":{"200":{"description":"The case and its removal effect.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`CASE_NOT_FOUND`: no case with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"moderation:queue.read","x-precrime-described":true}},"/v1/private/moderation/cases/{caseId}/escalate":{"post":{"operationId":"escalateVerdict","summary":"Build the operator's escalation of a provisional verdict","description":"The unsigned `CaseFactory.escalate(pool, reason)` call for the operator's wallet (D-086): it puts a provisional verdict before a person, freezing claims, and publishes the reason in full on chain, its sha256 kept by the pool. The pool takes it only while the verdict is not yet due: claims open once confirmed, claims plus the confirmation timeout until then. Nothing is recorded here; the chain's `CaseEscalated` is the record.","tags":["Moderation"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"caseId","in":"path","required":true,"description":"The case's public id (or internal id).","schema":{"type":"string"}}],"requestBody":{"description":"The reason, published on chain in full.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","description":"1 to 1,024 bytes of UTF-8, sent byte for byte: nothing is trimmed."}}}}}},"responses":{"200":{"description":"The call, the digest the pool will keep, and when escalation closes.","content":{"application/json":{"schema":{"type":"object","required":["caseId","pool","reasonDigest","escalateBy","call"],"properties":{"caseId":{"type":"string"},"pool":{"$ref":"#/components/schemas/EvmAddress"},"reasonDigest":{"type":"string","description":"sha256 of the reason's bytes."},"escalateBy":{"$ref":"#/components/schemas/Timestamp"},"call":{"type":"object","additionalProperties":true}}}}}},"400":{"description":"`INVALID_REASON`: empty, or over the factory's 1,024 bytes. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`CASE_NOT_FOUND`: no case with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`NOTHING_TO_ESCALATE`: no posted verdict on a pool. `ESCALATION_CLOSED`: the verdict is due, or the case is already in review.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`CHAIN_NOT_CONFIGURED`: no case factory is configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"review:challenge.resolve","x-precrime-described":true}},"/v1/private/moderation/cases/{caseId}/remove":{"post":{"operationId":"removeModerationCase","summary":"Remove a case from listings — this refunds nobody","description":"Delists a case and closes its reports. It does not move money, and the response is written so that reading it as though it had takes effort: `participantsRefunded` is present and false, and `onChainActionRequired` says whether a pool still holds stakes. The pool contract does not read this database; the only thing that can void it and refund participants is a MANUAL_REVIEW_ROLE holder calling `resolveManualReview` on chain. The request must echo `removalAcknowledgement` verbatim, the one field proving the operator was shown that.","tags":["Moderation"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"caseId","in":"path","required":true,"description":"The case to remove from listings.","schema":{"type":"string"}}],"requestBody":{"description":"The reason, and the acknowledgement echoed verbatim.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason","acknowledgement"],"properties":{"reason":{"type":"string","description":"Recorded permanently against the removal."},"acknowledgement":{"type":"string","description":"Exactly the `removalAcknowledgement` string from the queue response. Any other value is refused, and the refusal repeats what was expected."}}}}}},"responses":{"200":{"description":"What the removal did, and what it did not.","content":{"application/json":{"schema":{"type":"object","required":["caseId","participantsRefunded","onChainActionRequired","removal"],"properties":{"caseId":{"type":"string"},"participantsRefunded":{"type":"boolean","description":"Always false. Present precisely so nothing has to be inferred from its absence."},"onChainActionRequired":{"type":"boolean","description":"True when a pool still holds stakes. Until someone calls `resolveManualReview`, that money is exactly where it was."},"removal":{"type":"object","description":"The recorded removal, including any on-chain void still owed."},"suppression":{"type":"object","description":"What is now hidden from listings."},"suppressionDisclosure":{"type":"string","description":"The sentence shown to anyone who reaches the case by its old link."},"reportsClosed":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"`ACKNOWLEDGEMENT_REQUIRED` when the acknowledgement is absent or not verbatim — the response repeats the expected string — or a refusal naming the minimum reason length. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`CASE_NOT_FOUND`: no case with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"moderation:case.remove","x-precrime-described":true}},"/v1/private/moderation/disputes/{disputeId}":{"get":{"operationId":"getModerationDispute","summary":"One dispute and its receipt","description":"The dispute as it stands, including whether the pool has actually settled on chain. The two are separate facts: a recorded resolution is a decision, and settlement is what happened to the money.","tags":["Moderation"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"disputeId","in":"path","required":true,"description":"The dispute's id, as the queue reports it.","schema":{"type":"string"}}],"responses":{"200":{"description":"The dispute receipt.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`DISPUTE_NOT_FOUND`: no dispute with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"moderation:queue.read","x-precrime-described":true}},"/v1/private/moderation/disputes/{disputeId}/packet":{"get":{"operationId":"getDisputePacket","summary":"Everything needed to rule one review","description":"Why the review opened (D-086: the two machines disagreed, or the operator escalated), both machines side by side — the posting under review and the verifier's answer, each with its digest — the escalation's reason shown only when it hashes to the digest the pool holds, the resolver's report, the watchdog's finding, and the rulings the pool will accept (`rulingAllowed`: a refund, the verifier's side, or the posted side when a machine posted it), each with its unsigned `resolveManualReview` call. Nothing is re-evaluated here.","tags":["Moderation"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"disputeId","in":"path","required":true,"description":"The dispute the packet is for.","schema":{"type":"string"}}],"responses":{"200":{"description":"The review packet.","content":{"application/json":{"schema":{"type":"object","required":["disputeId","case","cause","machines","allowedRulings","onChainCall"],"properties":{"disputeId":{"type":"string"},"status":{"type":"string"},"case":{"type":"object","additionalProperties":true},"cause":{"type":"string","enum":["CHALLENGE","DISAGREEMENT","ESCALATION"],"description":"Why the review opened. `CHALLENGE` is a review from before D-086."},"reason":{"type":"object","description":"`digest`: what the pool stored (the verifier's report digest, or the sha256 of the escalation's text); `text`: the escalation's words, only when they hash to it; `openedBy`: the verifier or the operator; `txHash`: the transaction that opened it.","additionalProperties":true},"machines":{"type":"object","description":"`posted`: who posted which side with which digest, and the posting key's registry seat as read now (null when not read); `verifier`: its answer (`CONFIRMED` or `DISPUTED`), side and digest, or null.","additionalProperties":true},"allowedRulings":{"type":"array","items":{"type":"string","enum":["NONE","GUILTY","INNOCENT"]},"description":"The rulings the pool accepts, refund first. Without the posting key's seat, the posted side is offered only when the verifier reached it too: less, never more."},"rulingCalls":{"type":["object","null"],"description":"Each allowed ruling's unsigned `resolveManualReview(pool, side)` call (`to`, `data`, `value`), for the operator's wallet; null with no factory configured.","additionalProperties":true},"challenge":{"type":["object","null"],"additionalProperties":true},"watchdog":{"type":"object","description":"The watchdog's recorded finding on the pool (D-085, D-086), read from its own table: `NOT_CONFIGURED`, `NO_CHECK` (with `lastSeenAt`), or `CHECKED` with its row — `status`, the reason code `finding`, the resolver's verdict and digest as it read them (`resolver`), its own (`own`), the `head` it read at, and its answer's transaction (`answerTxHash`). `answeredThis` says the verifier's answer on chain is this watchdog's; `digestVerified` that its own reading is the digest on chain.","additionalProperties":true},"resolution":{"type":["object","null"],"additionalProperties":true},"onChainCall":{"type":"object","additionalProperties":true},"review":{"type":"object","description":"The review's deadline (G-002): `openedAt`, the pool's `reviewTimeoutSeconds`, and `voidableAt`, from which the pool refuses `resolveManualReview` with `ReviewTimedOut` and anyone may call its `voidStaleReview()` instead, named in `staleVoidCall`. `voidableAt` is null when the timeout is not on record.","additionalProperties":true}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`DISPUTE_NOT_FOUND`: no dispute with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"moderation:queue.read","x-precrime-described":true}},"/v1/private/moderation/disputes/{disputeId}/resolve":{"post":{"operationId":"resolveModerationDispute","summary":"Record a ruling, and get the call that carries it out","description":"Records the ruling and hands back the unsigned `CaseFactory.resolveManualReview(pool, ruling)` call for the operator's wallet; the server never signs. Only a ruling the pool allows is accepted (D-086): a refund, the verifier's side, or the posted side when a machine posted it, the posting key's seat read from the chain. `settledOnChain` reports whether the pool has actually settled, and a recorded decision does not make it true.","tags":["Moderation"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"disputeId","in":"path","required":true,"description":"The dispute being resolved.","schema":{"type":"string"}}],"requestBody":{"description":"The ruling and the reason.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["ruling","reason"],"properties":{"ruling":{"type":"string","enum":["NONE","GUILTY","INNOCENT"],"description":"The pool's side: `NONE` refunds everyone."},"reason":{"type":"string","description":"Recorded permanently against the decision."},"txHash":{"type":"string","description":"The `resolveManualReview` transaction, when the reviewer sent it before recording the decision. Recorded as their account of it; the case's own status still comes from the indexer reading the pool."}}}}}},"responses":{"200":{"description":"What was recorded, the call, and what the chain still owes.","content":{"application/json":{"schema":{"type":"object","required":["disputeId","caseId","outcome","settledOnChain","effect","call"],"properties":{"disputeId":{"type":"string"},"caseId":{"type":"string"},"challengeId":{"type":["string","null"]},"outcome":{"type":"string","enum":["GUILTY","INNOCENT","UNRESOLVED"]},"settledOnChain":{"type":"boolean","description":"Whether a reviewer recorded the transaction that made the call. False means the decision is recorded and, as far as this service was told, the money has not moved."},"txHash":{"type":["string","null"]},"effect":{"type":"object","description":"What this recorded, and what remains on chain."},"call":{"type":["object","null"],"description":"The unsigned call (`to`, `data`, `value`, `functionName`, `args`); null with no factory configured or no pool.","additionalProperties":true}}}}}},"400":{"description":"`INVALID_REQUEST` for an unknown ruling, or a refusal naming the minimum reason length. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`DISPUTE_NOT_FOUND`: no dispute with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`RULING_NOT_ALLOWED`: the pool would revert the ruling (D-086). `DISPUTE_NOT_OPEN`: the dispute is already decided. `REVIEW_TIMED_OUT`: the review timed out (G-002) — the pool refuses `resolveManualReview` from the review's opening plus `reviewTimeout`, and anyone may void the case with its `voidStaleReview()`. A decision whose transaction was sent before then is still recorded when `txHash` is given.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"review:challenge.resolve","x-precrime-described":true}},"/v1/private/moderation/queue":{"get":{"operationId":"getModerationQueue","summary":"The triaged report queue","description":"Open reports, grouped by subject and ordered by severity then age. A report alleging a case names a real person sorts above everything else, because that is the one harm a refund cannot undo. `permissions` is served alongside so a console can hide a control this account may not use instead of rendering one the API will refuse. `unusable` lists reports triage could not place and why — surfaced rather than dropped, since a report nobody can see is the same as a report nobody filed.","tags":["Moderation"],"security":[{"sessionBearer":[]}],"responses":{"200":{"description":"The queue at one consistent instant.","content":{"application/json":{"schema":{"type":"object","required":["observedAt","permissions","removalAcknowledgement","queue","disputes"],"properties":{"observedAt":{"$ref":"#/components/schemas/Timestamp"},"permissions":{"type":"array","items":{"type":"string"},"description":"What this account may actually do, so the console need not guess."},"removalAcknowledgement":{"type":"string","description":"The exact sentence a removal request must echo. It states that removing a case refunds nobody, and requiring it verbatim is what stops an operator clicking past it."},"queue":{"type":"object","required":["subjects","unusable","openReportCount","closedReportCount","totalOpenReports","truncated"],"properties":{"subjects":{"type":"array","items":{"type":"object"}},"unusable":{"type":"array","items":{"type":"object"},"description":"Reports triage could not place, each with the reason."},"openReportCount":{"type":"integer"},"closedReportCount":{"type":"integer"},"totalOpenReports":{"type":"integer","description":"Before any cap, so a truncated page still reports the real size."},"truncated":{"type":"boolean","description":"True when the list is capped. Stated, never silently applied."}}},"disputes":{"type":"array","items":{"type":"object"}},"disputeEffects":{"type":"object","description":"What resolving a dispute each way records, and what it leaves on chain."}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"moderation:queue.read","x-precrime-described":true}},"/v1/private/moderation/reports":{"get":{"operationId":"getModerationReports","summary":"Named reports, for confirming a dismissal landed","description":"The current state of the reports named in `ids`. `missing` lists ids with no row: an id the caller asked about that does not exist is a fact they need, not a gap to infer from a shorter array than they sent.","tags":["Moderation"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"ids","in":"query","required":true,"description":"Comma-separated report ids, up to 200.","schema":{"type":"string"}}],"responses":{"200":{"description":"The reports, and the ids that had no row.","content":{"application/json":{"schema":{"type":"object","required":["data","missing"],"properties":{"data":{"type":"array","items":{"type":"object"}},"missing":{"type":"array","items":{"type":"string"},"description":"Ids with no report. Listed rather than silently absent."}}}}}},"400":{"description":"`INVALID_REQUEST`: `ids` is absent, empty, or over 200 entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"moderation:queue.read","x-precrime-described":true}},"/v1/private/moderation/reports/dismiss":{"post":{"operationId":"dismissModerationReports","summary":"Dismiss reports, with a recorded reason","description":"Closes the named reports as requiring no action. The written reason is mandatory and is stored: a reason validated and then discarded would look like an audit trail and not be one. `skipped` names reports already closed, so a repeated call is safe and says what it actually changed rather than reporting success for work it did not do.","tags":["Moderation"],"security":[{"sessionBearer":[]}],"requestBody":{"description":"The reports to dismiss and why.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["itemIds","reason"],"properties":{"itemIds":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":200},"reason":{"type":"string","description":"Recorded against every dismissal. A floor length applies and the refusal names it."}}}}}},"responses":{"200":{"description":"What was dismissed and what was not.","content":{"application/json":{"schema":{"type":"object","required":["dismissedItemIds","skipped","effect"],"properties":{"dismissedItemIds":{"type":"array","items":{"type":"string"}},"skipped":{"type":"array","items":{"type":"object"},"description":"Already-closed reports, each with why it was left alone."},"effect":{"type":"object","description":"Exactly what this dismissal changed."}}}}}},"400":{"description":"`INVALID_REQUEST` for a malformed body, or a refusal naming the minimum reason length. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"moderation:item.resolve","x-precrime-described":true}},"/v1/private/ops/deployment":{"get":{"operationId":"getOpsDeployment","summary":"The deployment the owner panel reads","description":"Operator-only, behind the permission `/admin` asks for. The contract addresses, the D-018 holders, the timelock's members and the block its history starts at, from this service's own environment, so the web server's owner panel (`/admin/owner`) needs only its RPC (founder, 2026-10-03). Exactly the variables `@precrime/core`'s `OWNER_DEPLOYMENT_VARIABLES` names, every one present: an address, a comma-separated list of addresses, or a block, and null when unset or malformed. A malformed value is named in `malformed` and never echoed. Nothing else of the environment is served: no URL, key or token. The panel reads the chain itself and signs nothing; the wallet does.","tags":["Operations"],"security":[{"sessionBearer":[]}],"responses":{"200":{"description":"The deployment, as this service is configured.","content":{"application/json":{"schema":{"type":"object","required":["chainId","variables","malformed"],"properties":{"chainId":{"type":"integer","description":"This service's `CHAIN_ID`, so a web server configured for another chain can refuse."},"variables":{"type":"object","description":"Variable name to value: a checksummed address, addresses joined by commas, or a block number in decimal; null when unset or malformed.","additionalProperties":{"type":["string","null"]}},"malformed":{"type":"array","items":{"type":"string"},"description":"The variables set to something that is not what they read as. A list keeps its valid entries and is named here when any other entry was dropped."}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"jobs:inspect","x-precrime-described":true}},"/v1/private/ops/events":{"get":{"operationId":"listRewardEventDrafts","summary":"The events console: recent drafts, and the limits a new event must fit","description":"The fifty newest EVENT drafts, each with the exact `createEvent` call the events key sends, and the contract's limits read live: the per-event cap, what the rolling thirty days still allow, what the stream has unlocked, and the notice, with the earliest time claims may open for a draft made now. `limits` is null when the chain could not be read.","tags":["Operations"],"security":[{"sessionBearer":[]}],"responses":{"200":{"description":"The drafts and the limits.","content":{"application/json":{"schema":{"type":"object","required":["contract","chainId","limits","drafts"],"properties":{"contract":{"$ref":"#/components/schemas/EvmAddress"},"chainId":{"type":"integer"},"limits":{"type":["object","null"],"properties":{"blockNumber":{"type":"string"},"paused":{"type":"boolean"},"budgetCapBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"windowCapBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"windowSpentBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"windowRoomBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"availableBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"noticeSeconds":{"type":"integer"},"earliestStartsAt":{"$ref":"#/components/schemas/Timestamp"}}},"drafts":{"type":"array","items":{"$ref":"#/components/schemas/RewardEventDraft"}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: no contract address or RPC, so nothing could be drafted or recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"rewards:events.manage","x-precrime-described":true},"post":{"operationId":"draftRewardEvent","summary":"Draft an event from a winners list","description":"Reads `address,amount` lines, the amount in whole PRECRIME; refuses a malformed, zero, duplicated or checksum-failing address rather than repairing it. Builds the Merkle tree `CommunityRewards` verifies and the file whose keccak256 is the `dataHash`, and checks everything `createEvent` would revert over against the contract's live limits: the per-event cap, the rolling thirty days, what has unlocked, the notice (plus half an hour to sign), the window and the vesting, and whether the root was ever used. The server holds no key that can create the event: the answer carries the exact call for the events key to send. The same file drafted again answers 200 with the draft on record. The written reason goes to the audit log.","tags":["Operations"],"security":[{"sessionBearer":[]}],"requestBody":{"description":"The event.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","budget","startsAt","endsAt","csv","reason"],"properties":{"title":{"type":"string","description":"3 to 120 characters."},"budget":{"type":"string","pattern":"^\\d{1,12}$","description":"Whole PRECRIME; at least what the winners sum to."},"startsAt":{"type":"string","format":"date-time","description":"When claims open."},"endsAt":{"type":"string","format":"date-time","description":"When they close."},"vestForSeconds":{"type":"integer","minimum":0,"description":"0 pays at once."},"csv":{"type":"string","description":"`address,amount` lines; at most 5,000 entries."},"reason":{"type":"string","description":"12 to 2,000 characters, for the audit log."}}}}}},"responses":{"200":{"description":"The same file was drafted before; that draft.","content":{"application/json":{"schema":{"type":"object","required":["draft","limits"],"properties":{"draft":{"$ref":"#/components/schemas/RewardEventDraft"},"limits":{"type":"object","additionalProperties":true}}}}}},"201":{"description":"Drafted.","content":{"application/json":{"schema":{"type":"object","required":["draft","limits"],"properties":{"draft":{"$ref":"#/components/schemas/RewardEventDraft"},"limits":{"type":"object","additionalProperties":true}}}}}},"400":{"description":"`INVALID_REQUEST`, `INVALID_WINNERS` (with the lines in `problems`), `NO_WINNERS`, `TOO_MANY_WINNERS`, `INVALID_BUDGET`, `BUDGET_TOO_SMALL`, `OVER_EVENT_CAP`, `INVALID_WINDOW` or `NOTICE_TOO_SHORT`. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`OVER_WINDOW_CAP`, `NOT_UNLOCKED`, or `ROOT_USED`: a campaign already used this root.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: no contract address or RPC. `UNAVAILABLE`: the contract could not be read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"rewards:events.manage","x-precrime-described":true}},"/v1/private/ops/events/{id}/created":{"post":{"operationId":"recordRewardEventCreated","summary":"File the claims of an event the events key has created","description":"The console reads the campaign id from the transaction's `CampaignCreated` and posts it here; the service reads that campaign from the chain and files the draft's claims only when it is an EVENT with the draft's root, `dataHash`, budget and window exactly — a root is used once, so a campaign that matches is this draft's. Safe to repeat.","tags":["Operations"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The draft's id.","schema":{"type":"string"}}],"requestBody":{"description":"The campaign the transaction created.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["campaignId"],"properties":{"campaignId":{"type":"integer","minimum":0},"txHash":{"$ref":"#/components/schemas/Hash32"}}}}}},"responses":{"200":{"description":"Filed.","content":{"application/json":{"schema":{"type":"object","required":["campaignId","claims","filed","status"],"properties":{"campaignId":{"type":"integer"},"claims":{"type":"integer","description":"Leaves in the draft."},"filed":{"type":"integer","description":"Leaves written by this call."},"status":{"type":"string","enum":["CREATED"]}}}}}},"400":{"description":"`INVALID_REQUEST`. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such draft.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`NOT_ON_CHAIN` (not created yet), `CAMPAIGN_MISMATCH` (the campaign is not this draft), `ALREADY_CREATED` (the draft is another campaign), `CAMPAIGN_CONFLICT`, or `CONFLICT` (another request is recording the same campaign at this moment).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: no contract address or RPC. `UNAVAILABLE`: the contract could not be read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"rewards:events.manage","x-precrime-described":true}},"/v1/private/ops/events/{id}/file":{"get":{"operationId":"getRewardEventDraftFile","summary":"A draft's file, byte for byte","description":"What the event will commit to, for the operator to check before sending: its keccak256 is the draft's `dataHash`.","tags":["Operations"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The draft's id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The file.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"`INVALID_REQUEST`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such draft for this contract.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: no contract address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"rewards:events.manage","x-precrime-described":true}},"/v1/private/ops/jobs/replay":{"post":{"operationId":"replayDeadLetteredJob","summary":"Requeue one dead-lettered job","description":"Operator-only and audited. A written reason of at least twelve characters is required by the request schema, not merely encouraged: replaying a job that was dead-lettered for good cause can repeat a side effect, so the record of who decided it was safe has to exist before the replay does. A replay that changed nothing answers 409 with the store's own reason rather than 200, because telling an operator it worked is how a job stays stuck while somebody believes it is dealt with.","tags":["Operations"],"security":[{"sessionBearer":[]}],"requestBody":{"description":"The queue, the job, and why this replay is safe.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["queue","jobId","reason"],"properties":{"queue":{"type":"string","enum":["deliveries","outbox"],"description":"Which queue the job sits in."},"jobId":{"type":"string","description":"The job's id within that queue."},"reason":{"type":"string","description":"12 to 500 characters. The only part of the audit record that explains why: the rest can be reconstructed from the diff, the judgement cannot."}}}}}},"responses":{"200":{"description":"The job was requeued and the decision recorded.","content":{"application/json":{"schema":{"type":"object","required":["replayed","queue","jobId"],"properties":{"replayed":{"type":"boolean","description":"True. A refusal is a 409."},"queue":{"type":"string"},"jobId":{"type":"string"}}}}}},"400":{"description":"`INVALID_REQUEST`: unknown queue, or a reason shorter than 12 characters. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`NOT_REPLAYED`, with a `reason`: nothing changed. The job is not dead-lettered, does not exist, or has already been requeued. Reported as a refusal because the operator's instruction was not carried out.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"jobs:dead_letter.requeue","x-precrime-described":true}},"/v1/private/ops/orders":{"get":{"operationId":"opsListOrders","summary":"Orders on the rail, for an operator","description":"Up to 200, newest first, optionally one status. `needsAttention` marks money that did not match what was sold, a flag the indexer raised, and a cancelled order whose payment is still in the router's escrow waiting for a refund.","tags":["Operations"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"status","in":"query","description":"Only orders in this status.","schema":{"$ref":"#/components/schemas/PaymentOrderStatus"}}],"responses":{"200":{"description":"Orders.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/PaymentOrder"},{"type":"object","required":["userId","sponsorId","needsAttention"],"properties":{"userId":{"type":["string","null"],"description":"Null on a chain-only row."},"sponsorId":{"type":["string","null"]},"needsAttention":{"type":"boolean"}}}]}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"payments:read","x-precrime-described":true}},"/v1/private/ops/orders/{id}/cancel":{"post":{"operationId":"opsCancelOrder","summary":"Cancel an order and release its slot","description":"Operator-only and audited, with a written reason. An unpaid quote is simply closed. A paid order's money stays in the router's escrow: the operator's wallet must then send `refund(orderHash)`, which only ever returns the payer's own money to the payer, and the chain's `OrderRefunded` closes the order. A settled order has been split and cannot be cancelled.","tags":["Operations"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The order's id.","schema":{"type":"string"}}],"requestBody":{"description":"Why.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","description":"12 to 500 characters."}}}}}},"responses":{"200":{"description":"Cancelled, and what is left to do.","content":{"application/json":{"schema":{"type":"object","required":["order","refundRequired","next"],"properties":{"order":{"$ref":"#/components/schemas/PaymentOrder"},"refundRequired":{"type":"boolean"},"next":{"type":["string","null"],"description":"The refund to send, when one is owed."}}}}}},"400":{"description":"`INVALID_REQUEST`: a reason shorter than 12 characters. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`ORDER_NOT_CANCELLABLE` for an order already settled, refunded or expired; `ORDER_CHANGED` when the chain moved the order while it was being cancelled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"payments:manage","x-precrime-described":true}},"/v1/private/ops/partners":{"get":{"operationId":"opsListPartners","summary":"Every partner, for review","description":"Operator-only. Each partner with its owner's verified wallets and how many accounts the owner has referred through any code.","tags":["Operations"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"status","in":"query","description":"Only partners in this status.","schema":{"type":"string","enum":["PENDING","ACTIVE","ENDED"]}}],"responses":{"200":{"description":"Partners.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Partner"}}}}}}},"400":{"description":"`INVALID_REQUEST`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"sponsors:read","x-precrime-described":true}},"/v1/private/ops/partners/{id}/status":{"post":{"operationId":"opsSetPartnerStatus","summary":"Approve or end a partner, and set a launchpad's multiplier","description":"Operator-only and audited, with a written reason. An ACTIVE launchpad's multiplier applies to the season points of traders whose on-chain referrer is one of its wallets, and `pnpm points:partners` exports it into the season's config; an ACTIVE KOL's referred stakes are what `pnpm kol:grants` pays on. `multiplierBps` is for launchpads only, 10,000 to 20,000; null returns it to the season's default.","tags":["Operations"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The partner's id.","schema":{"type":"string"}}],"requestBody":{"description":"The new status and why.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["status","reason"],"properties":{"status":{"type":"string","enum":["PENDING","ACTIVE","ENDED"]},"multiplierBps":{"type":["integer","null"],"minimum":10000,"maximum":20000},"reason":{"type":"string","description":"12 to 500 characters."}}}}}},"responses":{"200":{"description":"The partner.","content":{"application/json":{"schema":{"type":"object","required":["partner"],"properties":{"partner":{"$ref":"#/components/schemas/Partner"}}}}}},"400":{"description":"`INVALID_REQUEST`, including a multiplier for a partner that is not a launchpad. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"sponsors:manage","x-precrime-described":true}},"/v1/private/ops/queues":{"get":{"operationId":"getOpsQueues","summary":"Queue and outbox state","description":"Operator-only, and gated twice: the credential gate lets the request reach the route, and the permission gate decides whether the person behind it may act. An API key passes the first and can never pass the second, because an operator action is a human decision. Every figure here exists to separate two situations that look identical from outside — a relay that has stopped publishing from one with nothing to publish, a stuck reclaimer from a healthy one. The outbox's `oldestPendingAt` and `lagSeconds` are null when nothing is waiting, which is not a lag of zero.","tags":["Operations"],"security":[{"sessionBearer":[]}],"responses":{"200":{"description":"Queue metrics at one consistent instant.","content":{"application/json":{"schema":{"type":"object","required":["observedAt","queues","outbox"],"properties":{"observedAt":{"allOf":[{"$ref":"#/components/schemas/Timestamp"}],"description":"The single instant every figure below was evaluated against."},"queues":{"type":"array","items":{"$ref":"#/components/schemas/QueueMetrics"}},"outbox":{"type":"object","required":["pending","oldestPendingAt","lagSeconds","abandoned"],"description":"The transactional outbox, counted directly because it is its own table rather than a job queue. An unpublished event is work that was promised to somebody and not done.","properties":{"pending":{"type":"integer","description":"Events not yet published."},"oldestPendingAt":{"$ref":"#/components/schemas/NullableTimestamp"},"lagSeconds":{"type":["integer","null"],"description":"Age of the oldest unpublished event. Null when nothing is waiting — a zero would imply something was measured."},"abandoned":{"type":"integer","description":"Events that exhausted their attempts. Only a human moves this down."}}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"jobs:inspect","x-precrime-described":true}},"/v1/private/ops/resolver":{"get":{"operationId":"getResolverStatus","summary":"Where the automated resolver is, case by case","description":"Assembled from rows only. This service holds no resolver key and no archive RPC, so the key's gas balance and the head it reads are reported as NOT AVAILABLE HERE rather than guessed at — the worker logs both when it starts, and refuses to post below `RESOLVER_MIN_GAS_ETH`. `mode` is NOT_CONFIGURED when no case has ever been tracked here, because an empty table is the only evidence this service has either way. The checkpoint's `status` is passed through exactly as the worker wrote it, lease encoding included.","tags":["Operations"],"security":[{"sessionBearer":[]}],"responses":{"200":{"description":"The resolver, as its rows describe it.","content":{"application/json":{"schema":{"type":"object","required":["mode","tracked","byStatus","lease","gasBalance"],"properties":{"mode":{"description":"Per mode, how many cases are tracked in it, or NOT_CONFIGURED.","oneOf":[{"type":"string","enum":["NOT_CONFIGURED"]},{"type":"array","items":{"type":"object","additionalProperties":true}}]},"tracked":{"type":"integer"},"byStatus":{"type":"object","additionalProperties":{"type":"integer"}},"lease":{"type":"array","items":{"type":"object","additionalProperties":true}},"oldestBlocked":{"type":["object","null"],"additionalProperties":true},"lastPosted":{"type":["object","null"],"additionalProperties":true},"gasBalance":{"type":"string","enum":["NOT AVAILABLE HERE"]},"gasBalanceDetail":{"type":"string"}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"jobs:inspect","x-precrime-described":true}},"/v1/private/ops/slashes":{"get":{"operationId":"listSlashCandidates","summary":"Cases showing evidence for a ground to slash the creator's bond","description":"D-010's grounds, found from records the product keeps: a void the resolver declared unresolvable for a reason the rule's author controlled, a rule another case open at the same time already committed to, and an upheld report whose category establishes a ground. A case whose bond has come back or been slashed is not listed. `holdEndsAt` is when the pool lets the creator take the bond back, and null while the case is open. A finding is a reason to propose a slash, never a slash: `SLASH_ROLE` is the timelock's, and this service holds no key for it.","tags":["Operations"],"security":[{"sessionBearer":[]}],"responses":{"200":{"description":"Cases with grounds, soonest hold end first.","content":{"application/json":{"schema":{"type":"object","required":["observedAt","timelock","factory","holdSeconds","data"],"properties":{"observedAt":{"$ref":"#/components/schemas/Timestamp"},"timelock":{"type":["string","null"],"description":"Null when TIMELOCK_ADDRESS is unset."},"factory":{"type":["string","null"]},"holdSeconds":{"type":"integer","description":"CasePool.CREATOR_BOND_HOLD."},"data":{"type":"array","items":{"type":"object","required":["caseId","title","poolAddress","status","bond","findings"],"properties":{"caseId":{"type":"string"},"title":{"type":"string"},"chainId":{"type":"integer"},"poolAddress":{"type":["string","null"]},"status":{"type":"string"},"bond":{"type":"object","additionalProperties":true},"holdEndsAt":{"type":["string","null"]},"findings":{"type":"array","items":{"type":"object","required":["ground","evidence"],"properties":{"ground":{"type":"string","enum":["DUPLICATE","MALFORMED","FABRICATED","MISLEADING","UNRESOLVABLE"]},"evidence":{"type":"string"}}}},"proposal":{"type":["object","null"],"additionalProperties":true}}}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"bonds:slash","x-precrime-described":true},"post":{"operationId":"recordSlashProposal","summary":"Publish a slash proposal the timelock holds","description":"Called by the console after the proposer scheduled `CaseFactory.slashCreatorBond` on the timelock from their own wallet. The operation is recomputed from the case's pool, the factory and the bounty, and recorded on the case's public history — ground, evidence and when it can run — only if the timelock reports that operation as waiting or ready. Recording the same operation twice changes nothing.","tags":["Operations"],"security":[{"sessionBearer":[]}],"requestBody":{"description":"The case, the ground, the evidence, the bounty and the schedule tx.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["caseId","ground","evidence","bounty"],"properties":{"caseId":{"type":"string"},"ground":{"type":"string","enum":["DUPLICATE","MALFORMED","FABRICATED","MISLEADING","UNRESOLVABLE"]},"evidence":{"type":"string","description":"20 to 1,900 characters, published on the case."},"bounty":{"type":"string","description":"Receives half the bond."},"txHash":{"type":"string","description":"The `schedule` transaction, when known. The timelock is read either way."}}}}}},"responses":{"200":{"description":"Recorded, or already recorded.","content":{"application/json":{"schema":{"type":"object","required":["recorded","operationId","state","readyAt"],"properties":{"recorded":{"type":"boolean","description":"False when it was already on the case."},"operationId":{"type":"string"},"state":{"type":"string","enum":["WAITING","READY"]},"readyAt":{"$ref":"#/components/schemas/Timestamp"}}}}}},"400":{"description":"`INVALID_REQUEST`: an unknown ground, short evidence, or a bad address. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no published case with that id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`NOT_SCHEDULED`: the timelock does not hold this slash, so there is nothing to announce.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: RPC_URL, CASE_FACTORY_ADDRESS or TIMELOCK_ADDRESS is unset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"bonds:slash","x-precrime-described":true}},"/v1/private/ops/sponsors":{"get":{"operationId":"opsListSponsors","summary":"Sponsor applications, for an operator","description":"Up to 200, newest first, optionally one status.","tags":["Operations"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"status","in":"query","description":"Only sponsors in this status.","schema":{"type":"string","enum":["PENDING","APPROVED","REJECTED","SUSPENDED"]}}],"responses":{"200":{"description":"Sponsors.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Sponsor"}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"sponsors:read","x-precrime-described":true}},"/v1/private/ops/sponsors/{id}/launch-partner":{"post":{"operationId":"opsGrantLaunchPartnerSlot","summary":"Give a launch partner its free sponsor slot","description":"Operator-only and audited, with a written reason (G-068, D-085). The first three launch partners get one slot each for nothing: at most three ever, one per sponsor, refused by the server and by the database. The slot is booked on the same rules as a paid one — an approved sponsor, a live surface, its reviewed creative, free inventory, not in the past — and goes live with nothing paid. It never reaches the payment router.","tags":["Operations"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The sponsor's id.","schema":{"type":"string"}}],"requestBody":{"description":"The slot, its window and why.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["slot","startsAt","endsAt","reason"],"properties":{"slot":{"type":"string"},"startsAt":{"$ref":"#/components/schemas/Timestamp"},"endsAt":{"$ref":"#/components/schemas/Timestamp"},"sends":{"type":"integer"},"reason":{"type":"string","description":"12 to 500 characters."}}}}}},"responses":{"201":{"description":"The live slot and the seat it took.","content":{"application/json":{"schema":{"type":"object","required":["sponsorship","seat","seats","listUsdCents","usdCents"],"properties":{"sponsorship":{"type":"object","additionalProperties":true},"seat":{"type":"integer","description":"1 to 3."},"seats":{"type":"integer"},"listUsdCents":{"type":"string","pattern":"^\\d+$"},"usdCents":{"type":"string","const":"0"}}}}}},"400":{"description":"`INVALID_REQUEST`; or a booking refusal — `UNKNOWN_SLOT`, `INVALID_RANGE`, `SEND_COUNT_REQUIRED`, `STARTS_IN_THE_PAST`, `INVALID_CREATIVE`. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`SPONSOR_NOT_APPROVED`; `LAUNCH_PARTNER_ALREADY_GRANTED`; `LAUNCH_PARTNER_SEATS_TAKEN` once all three are granted; `SLOT_AT_CAPACITY`; `SURFACE_NOT_LIVE`; `UNREADABLE_BOOKING`; `CREATIVE_INVALID`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"sponsors:manage","x-precrime-described":true}},"/v1/private/ops/sponsors/{id}/status":{"post":{"operationId":"opsSetSponsorStatus","summary":"Approve, reject or suspend a sponsor","description":"Operator-only and audited, with a written reason. Only an APPROVED sponsor can be quoted a slot; the review of the creative happens here, before any money moves.","tags":["Operations"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The sponsor's id.","schema":{"type":"string"}}],"requestBody":{"description":"The new status and why.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["status","reason"],"properties":{"status":{"type":"string","enum":["PENDING","APPROVED","REJECTED","SUSPENDED"]},"reason":{"type":"string","description":"12 to 500 characters."}}}}}},"responses":{"200":{"description":"The sponsor.","content":{"application/json":{"schema":{"type":"object","required":["sponsor"],"properties":{"sponsor":{"$ref":"#/components/schemas/Sponsor"}}}}}},"400":{"description":"`INVALID_REQUEST`. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"sponsors:manage","x-precrime-described":true}},"/v1/private/ops/stars":{"get":{"operationId":"listStarsPayments","summary":"Telegram Stars purchases of Pro, newest first, with the totals","description":"Every charge Telegram completed for Pro (D-054): the Stars, the plan it extended, any reason no plan was granted, and whether it was refunded. Off-chain revenue.","tags":["Operations"],"security":[{"sessionBearer":[]}],"responses":{"200":{"description":"The totals and the latest fifty payments.","content":{"application/json":{"schema":{"type":"object","required":["totals","payments"],"properties":{"totals":{"type":"object","additionalProperties":true},"payments":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"payments:read","x-precrime-described":true}},"/v1/private/ops/stars/{chargeId}/refund":{"post":{"operationId":"refundStarsPayment","summary":"Refund a Stars payment and take back the plan it bought","description":"Recorded before Telegram is asked: the payment is marked refunded, thirty days come off the Pro it extended (ending it if that is now), and `refundStarPayment` is queued. The written reason goes to the audit log.","tags":["Operations"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"chargeId","in":"path","required":true,"description":"Telegram's `telegram_payment_charge_id`.","schema":{"type":"string"}}],"requestBody":{"description":"Why this is refunded.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string","description":"8 to 1,000 characters."}}}}}},"responses":{"202":{"description":"Recorded, and the refund queued.","content":{"application/json":{"schema":{"type":"object","required":["refunded","chargeId"],"properties":{"refunded":{"type":"boolean"},"chargeId":{"type":"string"}}}}}},"400":{"description":"`INVALID_REQUEST`: no written reason. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` when the person behind the session holds no role granting the permission, or `OPERATOR_REQUIRES_SESSION` when an API key was presented. The two have different remedies, which is why they are different codes: a machine client told only `FORBIDDEN` will retry forever with a credential that can never work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no payment with that charge id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`ALREADY_REFUNDED`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"payments:manage","x-precrime-described":true}},"/v1/private/ops/verdicts":{"get":{"operationId":"listVerdictsDue","summary":"Cases due a verdict, with what the resolver concluded","description":"Live cases whose staking has closed and that have no verdict yet, lightning rounds first (D-084: their deadline is two hours after counting stops, and while their type shadows an operator posting by hand is the only way their verdict lands), then soonest deadline first, for whoever holds the manual resolver key (D-037). Each carries the resolver's own run — its verdict and the digest of its report, or why it has neither — and the pool's two sides, so a client can say before a signature that a one-sided case voids whichever way it goes (D-002). `pastDeadline` means the pool now refuses a verdict (D-048). Read-only: the call is sent from the resolver's own wallet, and this service holds no resolver key.","tags":["Operations"],"security":[{"sessionBearer":[]}],"responses":{"200":{"description":"Cases waiting on a verdict.","content":{"application/json":{"schema":{"type":"object","required":["observedAt","data"],"properties":{"observedAt":{"$ref":"#/components/schemas/Timestamp"},"data":{"type":"array","items":{"type":"object","required":["caseId","title","chainId","poolAddress","pastDeadline","pool","report"],"properties":{"caseId":{"type":"string"},"title":{"type":"string"},"chainId":{"type":"integer"},"poolAddress":{"type":["string","null"]},"stakeCutoffAt":{"type":["string","null"]},"resolutionDeadlineAt":{"type":["string","null"]},"windowEndsAt":{"type":["string","null"],"description":"When the rule's window ends: the cutoff for a version 2 rule, `until` for a stake-first one (D-067). Null when the stored rule could not be read."},"windowOpen":{"type":"boolean","description":"The window still counts after staking closed. Nothing may be posted until it has ended and the resolver has evaluated it; the pool itself checks only the cutoff."},"lightning":{"type":"boolean","description":"A lightning round (D-084), the server's reading of the pool's own times at publish. Listed first."},"pastDeadline":{"type":"boolean"},"pool":{"type":"object","additionalProperties":true},"report":{"type":["object","null"],"additionalProperties":true}}}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-operator-permission":"jobs:inspect","x-precrime-described":true}},"/v1/private/orders":{"get":{"operationId":"listOrders","summary":"The caller's orders, newest first","description":"Each order's status is the chain's: paid, settled and refunded come from the router.","tags":["Payments"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"parameters":[{"name":"limit","in":"query","description":"Page size, 1 to 100. Defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","description":"The `nextCursor` from the previous page. Cursors are opaque and positional rather than offsets: do not construct one, and do not assume two pages taken minutes apart line up.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of orders.","content":{"application/json":{"schema":{"type":"object","required":["data","nextCursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PaymentOrder"}},"nextCursor":{"type":["string","null"],"description":"Null on the last page."}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"orders:read","x-precrime-described":true},"post":{"operationId":"createOrder","summary":"Price, write and sign an order","description":"Session only: the payer is the session's own wallet, and an API key must never start a purchase. The body names what to buy and the token to pay in, and nothing else — any amount, family or referrer a client adds is dropped. The server prices from its catalogue, resolves the referrer from a finalised attribution, writes the order, then signs. Pay it with `PaymentRouter.pay(quote, signature)` from the payer's wallet within fifteen minutes. An API plan settles in the same transaction; a sponsor slot waits in the router until it starts, and until then can be refunded to its payer. A sponsor slot's blocks that start within 60 days of launch are half price (G-068, D-085): `pricing` states the list price, the price charged — the quote's amount — and the discount, as the order row records them. No discount a client sends is read.","tags":["Payments"],"security":[{"sessionBearer":[]}],"requestBody":{"description":"What to buy and which accepted token to pay in.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["product","tokenAddress"],"properties":{"product":{"type":"object","required":["kind"],"description":"`{ kind: \"API_PLAN\", tier }` or `{ kind: \"SPONSOR_SLOT\", slot, startsAt, endsAt, sends? }`.","properties":{"kind":{"type":"string","enum":["API_PLAN","SPONSOR_SLOT"]},"tier":{"type":"string"},"slot":{"type":"string"},"startsAt":{"$ref":"#/components/schemas/Timestamp"},"endsAt":{"$ref":"#/components/schemas/Timestamp"},"sends":{"type":"integer"}}},"tokenAddress":{"$ref":"#/components/schemas/EvmAddress"}}}}}},"responses":{"201":{"description":"The order, the quote and the signature to pay it with.","content":{"application/json":{"schema":{"type":"object","required":["order","quote","signature","typedData","router","chainId","token"],"properties":{"order":{"$ref":"#/components/schemas/PaymentOrder"},"quote":{"$ref":"#/components/schemas/PaymentQuote"},"signature":{"type":"string","description":"The quote signer's EIP-712 signature."},"typedData":{"type":"object","additionalProperties":true,"description":"The domain, types and message, as a wallet displays them."},"router":{"$ref":"#/components/schemas/EvmAddress"},"chainId":{"type":"integer"},"token":{"$ref":"#/components/schemas/AcceptedToken"},"pricing":{"type":"object","required":["listUsdCents","usdCents","discount"],"properties":{"listUsdCents":{"type":["string","null"],"pattern":"^\\d+$"},"usdCents":{"type":["string","null"],"pattern":"^\\d+$"},"discount":{"type":["object","null"],"additionalProperties":true,"description":"`{ rule: LAUNCH_PRICING, discountBps, discountedUnits, listUsdCents, discountUsdCents, endsAt }`, or null."}}}}}}}},"400":{"description":"`INVALID_REQUEST`; `TOKEN_NOT_ACCEPTED`; or a booking refusal — `UNKNOWN_SLOT`, `INVALID_RANGE`, `SEND_COUNT_REQUIRED`, `STARTS_IN_THE_PAST`, `INVALID_CREATIVE`. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN` with `requiredScope` for an API key, or `SPONSOR_NOT_APPROVED` for a slot requested by an account whose sponsor application is not approved.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`WALLET_REQUIRED` when the session has no wallet; `SLOT_AT_CAPACITY`; `SURFACE_NOT_LIVE`; `PLAN_NOT_PURCHASABLE`; `PLAN_PAUSED` for Research and Desk, which are not on sale for now (D-029); `UNREADABLE_BOOKING`; `CREATIVE_INVALID` when the approved creative no longer passes review.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`TOO_MANY_OPEN_QUOTES` at three unpaid quotes, or `RATE_LIMITED` / `QUOTA_EXCEEDED` as on every gated route.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`PAYMENTS_NOT_CONFIGURED` naming what is unset, or `PAYMENTS_DEGRADED` naming each failed check. Nothing is written or signed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"orders:write","x-precrime-described":true}},"/v1/private/orders/{id}":{"get":{"operationId":"getOrder","summary":"One of the caller's orders","description":"Another account's order answers 404, exactly as a missing one does.","tags":["Payments"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The order's id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentOrder"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"orders:read","x-precrime-described":true}},"/v1/private/orders/{id}/submitted":{"post":{"operationId":"markOrderSubmitted","summary":"Report the transaction the wallet sent","description":"Advisory only. The order becomes PAID when the indexer reads the router's `OrderPaid`, whatever is reported here; an order the chain has already moved on is returned unchanged.","tags":["Payments"],"security":[{"sessionBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The order's id.","schema":{"type":"string"}}],"requestBody":{"description":"The transaction hash.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["txHash"],"properties":{"txHash":{"type":"string","pattern":"^0x[0-9a-fA-F]{64}$"}}}}}},"responses":{"200":{"description":"The order.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentOrder"}}}},"400":{"description":"`INVALID_REQUEST`: not a transaction hash. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"orders:write","x-precrime-described":true}},"/v1/private/partners/apply":{"post":{"operationId":"applyAsPartner","summary":"Apply as a launchpad, KOL or group partner","description":"Names the codes this account already holds — its own referral code, or campaign codes made on /kol — as the partner's. A code the account does not hold is refused. Applying again, or changing anything, returns the partner to PENDING for an operator to review. A session only: an arrangement is a person's decision, not a delegated key's.","tags":["Partners"],"security":[{"sessionBearer":[]}],"requestBody":{"description":"The partner's name, kind and codes.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","kind","codes"],"properties":{"name":{"type":"string","description":"2 to 80 characters."},"kind":{"type":"string","enum":["LAUNCHPAD","KOL","GROUP"]},"codes":{"type":"array","items":{"type":"string"},"description":"1 to 20 codes."},"contact":{"type":"string","description":"How to reach them. Only operators see it."},"pitch":{"type":"string","description":"What they will do, up to 1,000 characters."}}}}}},"responses":{"201":{"description":"The partner, PENDING.","content":{"application/json":{"schema":{"type":"object","required":["partner"],"properties":{"partner":{"$ref":"#/components/schemas/Partner"}}}}}},"400":{"description":"`INVALID_REQUEST`, including a code the account does not hold. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"referrals:read","x-precrime-described":true}},"/v1/private/partners/me":{"get":{"operationId":"getMyPartnership","summary":"The caller's partner record and its figures","description":"The partner record, the codes the account could name, and — for a partner — what its codes brought: attributions per code, the referred accounts' stakes and the part in settled cases, per asset and never added across assets, and the owner's referral earnings: `earnings`, the ledger of the router's sponsor and API commissions, and `caseReferrals`, what each case pool holds for the owner's verified wallets. Both are the owner's whole, because a pool credits a referrer, not a link. A launchpad also sees its multiplier and how many traders name one of its wallets as referrer on the chain's registry, which is who the multiplier applies to.","tags":["Partners"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"The partner or null, the account's codes, and the figures.","content":{"application/json":{"schema":{"type":"object","required":["partner","availableCodes","dashboard"],"properties":{"partner":{"oneOf":[{"$ref":"#/components/schemas/Partner"},{"type":"null"}]},"availableCodes":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"active":{"type":"boolean"}}}},"dashboard":{"type":["object","null"],"properties":{"caseReferrals":{"$ref":"#/components/schemas/CaseReferrals"}}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"referrals:read","x-precrime-described":true}},"/v1/private/positions":{"get":{"operationId":"listMyPositions","summary":"The caller's positions","description":"Every position with the implied odds the call was made at, which is what lets a result be judged against the consensus at entry rather than with hindsight. Exact amounts are on the `…BaseUnits` fields; `payoutBaseUnits` is null until settlement, which is not a payout of zero. Capped at the most recent 200 and not paginated.","tags":["Cases"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"Positions, newest first.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Position"}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"cases:read","x-precrime-described":true}},"/v1/private/profile":{"get":{"operationId":"getProfile","summary":"The caller's reputation and activity","description":"Reputation is computed from settled positions only; open positions count towards activity and nothing else, because an unsettled call has not been right or wrong yet. The embedded positions carry `stake` for display only — `GET /v1/private/positions` is the read that carries exact base units.","tags":["Account"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"The profile.","content":{"application/json":{"schema":{"type":"object","required":["handle","joinedAt","walletAddress","reputation","activity","positions"],"properties":{"handle":{"type":["string","null"],"description":"The public handle, or null when the account record could not be read."},"joinedAt":{"$ref":"#/components/schemas/NullableTimestamp"},"walletAddress":{"type":["string","null"],"description":"Null when the credential is a key, which carries no wallet."},"reputation":{"$ref":"#/components/schemas/Reputation"},"activity":{"type":"object","required":["casesCreated","positionsOpen","positionsSettled"],"properties":{"casesCreated":{"type":"integer"},"positionsOpen":{"type":"integer"},"positionsSettled":{"type":"integer"}}},"positions":{"type":"array","description":"Up to the 50 most recent positions, for display.","items":{"type":"object","required":["caseId","caseTitle","caseStatus","side","stake","assetSymbol","entryImpliedProbabilityBps","settledAt"],"properties":{"caseId":{"type":"string"},"caseTitle":{"type":"string"},"caseStatus":{"$ref":"#/components/schemas/CaseStatus"},"side":{"$ref":"#/components/schemas/PositionSide"},"stake":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}],"description":"Formatted with the case's decimals; null for an unscalable row."},"assetSymbol":{"type":["string","null"],"description":"What the stake is counted in."},"entryImpliedProbabilityBps":{"type":["integer","null"],"description":"The odds at entry, or null when no snapshot was taken."},"settledAt":{"$ref":"#/components/schemas/NullableTimestamp"}}}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"cases:read","x-precrime-described":true}},"/v1/private/referrals":{"get":{"operationId":"getReferrals","summary":"The caller's referral code, attributions and earnings","description":"Attribution counts are reported by state rather than as one total, because a pending attribution is not an earned one. Balances are sums of ledger entries in base units, one per asset, over the whole ledger; reversals are entries too, so each balance is the ledger rather than a separate figure that could disagree with it. The ledger holds the router's sponsor and API commissions; what the case pools hold for the account's verified wallets is `caseReferrals`. The terms are returned alongside: the referral share is paid from preCrime's own fee, nothing is taken from a participant's stake, a void case pays nothing, and a signup on its own earns nothing.","tags":["Account"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"Referral state.","content":{"application/json":{"schema":{"type":"object","required":["code","attribution","earnings","caseReferrals","campaigns","terms"],"properties":{"caseReferrals":{"$ref":"#/components/schemas/CaseReferrals"},"code":{"type":["string","null"],"description":"The caller's referral code, or null when the account could not be read."},"attribution":{"type":"object","required":["pending","finalized","rejected"],"description":"Counts by state. Pending is not yet earned; rejected never will be.","properties":{"pending":{"type":"integer"},"finalized":{"type":"integer"},"rejected":{"type":"integer"}}},"earnings":{"type":"object","required":["balances","entries"],"properties":{"balances":{"type":"array","description":"One balance per asset. A USDG commission and a PRECRIME one are never added.","items":{"type":"object","required":["asset","symbol","decimals","balanceBaseUnits","balance"],"properties":{"asset":{"type":"string","description":"The asset code, or `USD` for cents."},"symbol":{"type":["string","null"]},"decimals":{"type":["integer","null"]},"balanceBaseUnits":{"type":"string","pattern":"^-?\\d+$","description":"The ledger sum in base units. A string for the reason every amount here is a string, and signed because a reversal is a negative entry."},"balance":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}],"description":"The balance for display, clamped at zero while `balanceBaseUnits` keeps the sign. Null for an asset the product cannot scale."}}}},"entries":{"type":"array","description":"Up to the 100 most recent ledger entries, newest first.","items":{"type":"object","required":["kind","source","asset","assetSymbol","amountBaseUnits","amount","reason","caseId","at"],"properties":{"kind":{"type":"string","description":"Entry kind, including reversals."},"source":{"type":"string","description":"Which revenue the entry came from."},"asset":{"type":"string","description":"The asset code the amount is denominated in."},"assetSymbol":{"type":["string","null"]},"amountBaseUnits":{"type":"string","pattern":"^-?\\d+$","description":"Signed, exact."},"amount":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}],"description":"Formatted with the asset's decimals; null when unknown."},"reason":{"type":"string","description":"Why the entry exists, in words."},"caseId":{"type":["string","null"],"description":"Set for case-settlement entries so an earning links to its case."},"at":{"$ref":"#/components/schemas/Timestamp"}}}}}},"campaigns":{"type":"array","items":{"type":"object","required":["code","name","clicks","telegramStarts","active"],"properties":{"code":{"type":"string"},"name":{"type":"string"},"clicks":{"type":"integer"},"telegramStarts":{"type":"integer"},"active":{"type":"boolean"}}}},"terms":{"type":"object","required":["caseReferralBps","note"],"properties":{"caseReferralBps":{"$ref":"#/components/schemas/Bps"},"note":{"type":"string","description":"What is paid, from where, and what is not."}}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"referrals:read","x-precrime-described":true}},"/v1/private/referrals/campaigns":{"get":{"operationId":"listReferralCampaigns","summary":"The caller's campaign codes","description":"A campaign is a second code that rolls up to the same owner, so a creator can tell one audience apart from another without being paid twice. Performance is counted from attribution rows, which are the only record of it that exists: a finalised attribution is a person who verified a wallet. `visits` and `telegramStarts` count arrivals through the link — a browser's first click, a /start — which is not the same thing and is reported beside it.","tags":["Referrals"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"The campaigns, newest first.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"type":"object","required":["code","name","active","performance"],"properties":{"code":{"type":"string"},"name":{"type":"string"},"active":{"type":"boolean"},"landingPath":{"type":["string","null"]},"createdAt":{"allOf":[{"$ref":"#/components/schemas/Timestamp"}]},"disabledAt":{"type":["string","null"],"format":"date-time"},"performance":{"type":"object","required":["visits","telegramStarts","finalized","pending"],"properties":{"visits":{"type":"integer","description":"Browsers that followed the link, each counted on its first click."},"telegramStarts":{"type":"integer","description":"/start commands in the bot that carried this code."},"finalized":{"type":"integer","description":"People this code brought who verified a wallet."},"pending":{"type":"integer","description":"Clicks that have not verified a wallet, so not yet an audience."}}}}}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"referrals:read","x-precrime-described":true},"post":{"operationId":"createReferralCampaign","summary":"Claim a campaign code","description":"The code is claimed, not assigned, and must be free in both namespaces: campaign codes and account referral codes are redeemed at the same `/r/<code>` route, so a collision would hand one person's audience to another. Fifty campaigns per account.","tags":["Referrals"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"requestBody":{"description":"The name and the code.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","code"],"properties":{"name":{"type":"string","description":"Up to 80 characters, for the owner's own use."},"code":{"type":"string","description":"3 to 32 characters: lowercase letters, digits, hyphen or underscore."},"landingPath":{"type":"string","description":"Where this campaign's links point by default. A path on this site."}}}}}},"responses":{"201":{"description":"The campaign.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"`INVALID_REQUEST`: the name or code failed validation. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`CODE_TAKEN` when the code is already an account's or another campaign's, and `TOO_MANY_CAMPAIGNS` at the fifty-campaign ceiling.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"referrals:read","x-precrime-described":true}},"/v1/private/referrals/campaigns/{code}/disable":{"post":{"operationId":"disableReferralCampaign","summary":"Switch a campaign off","description":"Disabled, never deleted: links already shared keep attributing to the owner, and what the code brought in stays readable. Disabling one that is already off is not an error and says so. A code belonging to another account answers the same 404 as one that does not exist.","tags":["Referrals"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"parameters":[{"name":"code","in":"path","required":true,"description":"The campaign code.","schema":{"type":"string"}}],"responses":{"200":{"description":"The campaign's new state.","content":{"application/json":{"schema":{"type":"object","required":["code","active","alreadyDisabled"],"properties":{"code":{"type":"string"},"active":{"type":"boolean","const":false},"alreadyDisabled":{"type":"boolean"}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NO_SUCH_CAMPAIGN`: no campaign with that code belongs to the caller.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"referrals:read","x-precrime-described":true}},"/v1/private/referrals/commissions":{"get":{"operationId":"listCommissions","summary":"What the payment router owes the caller as a referrer","description":"Read live from the router, per accepted token, for the account's first verified wallet on this chain — the wallet a quote names as referrer. Claim with `claimReferral(token, to)` from that wallet. `UNAVAILABLE` for a token the chain could not be read for. The earned entries are the ledger's record of sponsor and API commissions.","tags":["Account"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"Commissions owed and earned.","content":{"application/json":{"schema":{"type":"object","required":["status","reasons","router","wallet","tokens","earned"],"properties":{"status":{"type":"string","enum":["CONFIGURED","DEGRADED","NOT_CONFIGURED"]},"reasons":{"type":"array","items":{"type":"string"}},"router":{"oneOf":[{"$ref":"#/components/schemas/EvmAddress"},{"type":"null"}]},"wallet":{"type":["string","null"]},"tokens":{"type":"array","items":{"type":"object","required":["address","symbol","decimals","owedBaseUnits","owed","source"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"symbol":{"type":"string"},"decimals":{"type":"integer"},"owedBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"null"}]},"owed":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}]},"source":{"type":"string","enum":["CHAIN","UNAVAILABLE","NO_WALLET"]}}}},"earned":{"type":"array","items":{"type":"object","required":["kind","source","asset","amountBaseUnits","reason","at"],"properties":{"kind":{"type":"string"},"source":{"type":"string","enum":["SPONSOR_INVOICE","API_PURCHASE"]},"asset":{"type":"string"},"amountBaseUnits":{"type":"string","pattern":"^-?\\d+$","description":"Signed, exact."},"reason":{"type":"string"},"sourceTxHash":{"type":["string","null"]},"at":{"$ref":"#/components/schemas/Timestamp"}}}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"referrals:read","x-precrime-described":true}},"/v1/private/referrals/referrer":{"get":{"operationId":"getOnChainReferrer","summary":"The referrer the caller's stakes should name on chain","description":"CasePool pays its referral share only to `ReferralRegistry.referrerOf(staker)`, which only the staker's own wallet can set, once, with `register(referrer)` (D-006). This names that referrer: the account's finalised referral attribution and the referrer's first verified wallet on this chain. The stake panel reads the registry itself and offers the transaction when the wallet has no referrer yet. `referrer` is null with a reason when the account arrived through no link, the attribution was rejected, or the referrer has no verified wallet here yet.","tags":["Referrals"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"The referrer, or why there is none.","content":{"application/json":{"schema":{"type":"object","required":["chainId","referrer","reason"],"properties":{"chainId":{"type":"integer"},"referrer":{"type":["object","null"],"required":["address","code"],"properties":{"address":{"allOf":[{"$ref":"#/components/schemas/EvmAddress"}]},"code":{"type":["string","null"],"description":"The campaign or referral code the account arrived through."}}},"reason":{"oneOf":[{"enum":["NO_REFERRER","REFERRER_HAS_NO_WALLET","NOT_CONFIGURED"]},{"type":"null"}],"description":"Null when a referrer is named."}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"referrals:read","x-precrime-described":true}},"/v1/private/sponsors/apply":{"post":{"operationId":"applyAsSponsor","summary":"Apply to sponsor, or submit a new creative for review","description":"Session only. The creative is plain text and one https link: a headline of at most 80 characters, no markup, no control characters, and no credentials in the link. Applying again returns the sponsor to PENDING, because a creative nobody has reviewed is not one a slot can be sold for; placements already booked keep the creative they were booked with.","tags":["Sponsorships"],"security":[{"sessionBearer":[]}],"requestBody":{"description":"The sponsor and the creative.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","creative"],"properties":{"name":{"type":"string","description":"2 to 80 characters."},"contactEmail":{"type":"string","format":"email"},"creative":{"type":"object","required":["headline"],"properties":{"headline":{"type":"string"},"url":{"type":["string","null"]},"featuredCaseId":{"type":["string","null"]}}}}}}}},"responses":{"201":{"description":"The application, PENDING review.","content":{"application/json":{"schema":{"type":"object","required":["sponsor"],"properties":{"sponsor":{"$ref":"#/components/schemas/Sponsor"}}}}}},"400":{"description":"`INVALID_REQUEST`, or `INVALID_CREATIVE` with the rule broken in `reason`. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"orders:write","x-precrime-described":true}},"/v1/private/sponsors/me":{"get":{"operationId":"getMySponsor","summary":"The caller's sponsor application","description":"Null when the account has never applied.","tags":["Sponsorships"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"The sponsor, or null.","content":{"application/json":{"schema":{"type":"object","required":["sponsor"],"properties":{"sponsor":{"oneOf":[{"$ref":"#/components/schemas/Sponsor"},{"type":"null"}]}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"orders:read","x-precrime-described":true}},"/v1/private/telegram/link":{"delete":{"operationId":"unlinkTelegram","summary":"Unlink this account's Telegram chat","description":"Removes this account's Telegram link (D-088), whichever Telegram user it is; the request carries nothing. Needs a session signed in with a wallet, as linking does. In one serializable transaction it removes the link, withdraws every message for this account that was still waiting for that chat (alerts, operator pages, the link's own first message), ends any `/link` token that Telegram user still held and any session a Telegram sign-in opened on the account, writes `telegram.unlink` to the audit log, and queues the bot's word to the chat naming the wallet. What is the chat's own stays: the bot's replies, the daily briefing, and reminders asked for on single lightning cases. The account keeps its alert rules, mutes and quiet hours, and they apply again from a relink through the bot's `/link`.","tags":["Identity"],"security":[{"sessionBearer":[]}],"responses":{"200":{"description":"Unlinked.","content":{"application/json":{"schema":{"type":"object","required":["unlinked","telegram"],"properties":{"unlinked":{"type":"boolean","description":"Always true."},"telegram":{"type":"object","required":["username","firstName"],"properties":{"username":{"type":["string","null"]},"firstName":{"type":["string","null"]}},"description":"The Telegram user the account was linked to, as Telegram named them."}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`WALLET_SESSION_REQUIRED`: the session was not signed in with a wallet (a Telegram sign-in), or an API key was presented. Neither can unlink a chat.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_LINKED`: this account has no Telegram chat linked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`UNLINK_RACED` when a concurrent link or unlink kept winning: retry. (`SOLE_SIGN_IN`, an account with no wallet, is the bot's refusal and cannot come from here: a wallet's session always has one.)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true},"get":{"operationId":"getTelegramLink","summary":"Whether this account has linked Telegram","description":"The Telegram user this account is linked to, by the name Telegram gave when they linked, or `linked: false`. Alerts, the operator pages and the bot's account commands reach an account only through this link, so this is the read that says whether they can.","tags":["Identity"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"The link, or its absence.","content":{"application/json":{"schema":{"type":"object","required":["linked","telegram","linkedAt"],"properties":{"linked":{"type":"boolean"},"telegram":{"type":["object","null"],"required":["username","firstName"],"properties":{"username":{"type":["string","null"]},"firstName":{"type":["string","null"]}},"description":"Null when not linked."},"linkedAt":{"type":["string","null"],"format":"date-time","description":"When the link was first made; null when not linked."}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"account:read","x-precrime-described":true},"post":{"operationId":"linkTelegram","summary":"Link the Telegram chat a /link token names to this account","description":"Completes the bot's `/link`. The Telegram user and the chat are read from the challenge the bot wrote when that person asked, never from the request, and the challenge is consumed exactly once, in a serializable transaction, before it expires. Needs a session signed in with a wallet: a Telegram sign-in cannot vouch for itself, and a key is a machine credential. The link is written to the audit log, and the bot's first message to the chat names the wallet, so the person who asked can see whose account it joined. A re-link of the same pair succeeds with `relinked: true` and refreshes the chat; the same account presenting a token it already used is told it is linked. Every refusal leaves the token as it was.","tags":["Identity"],"security":[{"sessionBearer":[]}],"requestBody":{"description":"The token from the bot's link, and nothing else.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string","description":"The `link` parameter, verbatim."}}}}}},"responses":{"200":{"description":"Linked already: a re-link of the same pair, or a second press.","content":{"application/json":{"schema":{"type":"object","required":["linked","relinked","telegram","linkedAt"],"properties":{"linked":{"type":"boolean","description":"Always true."},"relinked":{"type":"boolean","description":"Always true here."},"telegram":{"type":"object","required":["username","firstName"],"properties":{"username":{"type":["string","null"]},"firstName":{"type":["string","null"]}}},"linkedAt":{"$ref":"#/components/schemas/Timestamp"}}}}}},"201":{"description":"Linked.","content":{"application/json":{"schema":{"type":"object","required":["linked","relinked","telegram","linkedAt"],"properties":{"linked":{"type":"boolean","description":"Always true."},"relinked":{"type":"boolean","description":"Always false here."},"telegram":{"type":"object","required":["username","firstName"],"properties":{"username":{"type":["string","null"]},"firstName":{"type":["string","null"]}}},"linkedAt":{"$ref":"#/components/schemas/Timestamp"}}}}}},"400":{"description":"`INVALID_REQUEST`: no token. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`WALLET_SESSION_REQUIRED`: the session was not signed in with a wallet (a Telegram sign-in), or an API key was presented. Neither can link a chat.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`LINK_TOKEN_UNKNOWN`: no link challenge has this token — mistyped, cut short, or a wallet challenge's nonce.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`TELEGRAM_LINKED_ELSEWHERE` when the Telegram user is linked to another account, `ACCOUNT_LINKED_ELSEWHERE` when this account is linked to another Telegram user, or `LINK_RACED` when concurrent links for the same token, user or account kept winning: retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"`LINK_TOKEN_USED` when another account used it (each works once), `LINK_TOKEN_EXPIRED` after fifteen minutes. Send `/link` again for a new one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/private/telegram/link/preview":{"post":{"operationId":"previewTelegramLink","summary":"Who a /link token would link, before linking","description":"Reads the challenge the bot wrote for the token and names the Telegram user it would link, so the page can show whose chat this is before anything is pressed: a token somebody else sent shows somebody else's name. Spends nothing. Refuses exactly as the link itself would. Needs a session signed in with a wallet.","tags":["Identity"],"security":[{"sessionBearer":[]}],"requestBody":{"description":"The token from the bot's link, and nothing else.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string","description":"The `link` parameter, verbatim."}}}}}},"responses":{"200":{"description":"The Telegram user the token names.","content":{"application/json":{"schema":{"type":"object","required":["telegram","expiresAt","alreadyLinked"],"properties":{"telegram":{"type":"object","required":["username","firstName"],"properties":{"username":{"type":["string","null"]},"firstName":{"type":["string","null"]}}},"expiresAt":{"$ref":"#/components/schemas/Timestamp"},"alreadyLinked":{"type":"boolean","description":"This account is linked to this Telegram user already; nothing to press."}}}}}},"400":{"description":"`INVALID_REQUEST`: no token. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`WALLET_SESSION_REQUIRED`: the session was not signed in with a wallet (a Telegram sign-in), or an API key was presented. Neither can link a chat.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`LINK_TOKEN_UNKNOWN`: no link challenge has this token — mistyped, cut short, or a wallet challenge's nonce.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`TELEGRAM_LINKED_ELSEWHERE` when the Telegram user is linked to another account, `ACCOUNT_LINKED_ELSEWHERE` when this account is linked to another Telegram user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"`LINK_TOKEN_USED` when another account used it (each works once), `LINK_TOKEN_EXPIRED` after fifteen minutes. Send `/link` again for a new one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/private/watchlist":{"get":{"operationId":"listWatchlist","summary":"Addresses the caller follows","description":"Each row carries its open-case count, so the list is useful rather than a bare set of hex the reader has to look up one address at a time. Capped at the most recent 250 and not paginated.","tags":["Watchlist"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"The watchlist, newest first.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"type":"object","required":["id","address","chain","displayName","createdAt","activeCases"],"properties":{"id":{"type":"string","description":"The row id, for removal."},"address":{"$ref":"#/components/schemas/EvmAddress"},"chain":{"$ref":"#/components/schemas/Chain"},"displayName":{"type":["string","null"],"description":"The watcher's own label. Null when none was given."},"createdAt":{"$ref":"#/components/schemas/Timestamp"},"activeCases":{"type":"integer","description":"Open cases naming this address."}}}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"cases:read","x-precrime-described":true},"post":{"operationId":"addToWatchlist","summary":"Follow an address","description":"Idempotent on the address: following one already followed updates the label instead of creating a duplicate row, so a retried request cannot produce two entries for one address.","tags":["Watchlist"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"requestBody":{"description":"The address, and optionally a private label for it.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["address"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"label":{"type":"string","description":"Up to 80 characters. Visible only to the caller."}}}}}},"responses":{"201":{"description":"The row, created or updated.","content":{"application/json":{"schema":{"type":"object","required":["id","address","chain","displayName","createdAt"],"properties":{"id":{"type":"string"},"address":{"$ref":"#/components/schemas/EvmAddress"},"chain":{"allOf":[{"$ref":"#/components/schemas/Chain"}],"description":"Always `ROBINHOOD_MAINNET` today: the route does not yet take a chain."},"displayName":{"type":["string","null"]},"createdAt":{"$ref":"#/components/schemas/Timestamp"}}}}}},"400":{"description":"`INVALID_REQUEST`: the address is not an EVM address. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"cases:write","x-precrime-described":true}},"/v1/private/watchlist/{id}":{"delete":{"operationId":"removeFromWatchlist","summary":"Stop following an address","description":"Removes the caller's own row. Another user's row is a 404, never a 403.","tags":["Watchlist"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The watchlist row id.","schema":{"type":"string"}}],"responses":{"204":{"description":"Done. No body, because there is nothing to say that the status has not said."},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"cases:write","x-precrime-described":true}},"/v1/private/webhooks":{"get":{"operationId":"listWebhooks","summary":"List delivery destinations","description":"The caller's endpoints with their consecutive failure count, so a destination that has been quietly failing is visible without reading delivery logs.","tags":["Webhooks"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"responses":{"200":{"description":"Endpoints.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}}}}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"webhooks:read","x-precrime-described":true},"post":{"operationId":"createWebhook","summary":"Register a delivery destination","description":"The URL is checked twice before it is stored: once for shape, to reject obviously internal destinations, and once against DNS, to reject a public-looking hostname that already resolves into private space. Neither check closes DNS rebinding, so the delivery worker validates the resolved address again at connect time. The signing secret is returned exactly once. Registration needs signing to be configured; when it is not, the answer is a 503 rather than an endpoint that would accept events and sign nothing.","tags":["Webhooks"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"requestBody":{"description":"The destination and the event types to send it.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri","description":"A public HTTPS destination."},"events":{"type":"array","items":{"type":"string"},"description":"1 to 25 event types."}}}}}},"responses":{"201":{"description":"The endpoint, with its signing secret for the only time.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEndpoint"}],"type":"object","required":["secret"],"properties":{"secret":{"type":"string","description":"Sign-check deliveries with this. Shown once; only a hash and an encrypted copy are retained."}}}}}},"400":{"description":"`INVALID_REQUEST` for a malformed body, or `PRIVATE_WEBHOOK_DESTINATION` with a `reason` when the URL is internal or resolves into private address space. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`WEBHOOK_SIGNING_NOT_CONFIGURED`: no signing key is configured, so an endpoint registered now would receive unsigned deliveries. Refused rather than degraded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"webhooks:write","x-precrime-described":true}},"/v1/private/webhooks/{id}":{"delete":{"operationId":"disableWebhook","summary":"Disable a delivery destination","description":"Sets the endpoint to `DISABLED` rather than deleting it, so delivery history keeps pointing at a destination that still exists.","tags":["Webhooks"],"security":[{"sessionBearer":[]},{"apiKeyBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The endpoint id from the list read.","schema":{"type":"string"}}],"responses":{"204":{"description":"Done. No body, because there is nothing to say that the status has not said."},"401":{"description":"`UNAUTHORIZED`: no bearer token, or one that is expired, revoked or unknown. Retrying with the same credential never helps.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`FORBIDDEN`: the credential is missing a scope, named in `requiredScope`. A key can be re-minted with that scope; a session-only scope can never be held by a key at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no such record, or one this credential may not read. The two are deliberately indistinguishable, so a 404 cannot be used to probe for records belonging to somebody else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED` when the per-credential window is exhausted, or `QUOTA_EXCEEDED`, carrying `resetAt`, when an API key's daily allowance is spent. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-required-scope":"webhooks:write","x-precrime-described":true}},"/v1/ranks":{"get":{"operationId":"listRanks","summary":"Rank thresholds","description":"The score boundaries of each rank tier, served so a client cannot drift from the server's definition of them.","tags":["Aggregates"],"responses":{"200":{"description":"The tiers.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RankTier"}}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/referrals/{code}/visit":{"post":{"operationId":"countReferralVisit","summary":"Count one arrival through a referral link","description":"Called by the web's `/r/<code>` redirect the first time a browser follows a link. Counts a visit on the campaign the code names, if any; an account's own code has no counter. Answers 204 either way, so it says nothing about which codes exist. Never an attribution: only a verified wallet makes one.","tags":["Referrals"],"parameters":[{"name":"code","in":"path","required":true,"description":"The referral or campaign code.","schema":{"type":"string"}}],"responses":{"204":{"description":"Counted, or nothing to count."},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/revenue/distributions":{"get":{"operationId":"listRevenueDistributions","summary":"Every split the fee splitters made, newest first","description":"Optionally one splitter and one asset. Each row is one asset; none adds two.","tags":["Aggregates"],"parameters":[{"name":"limit","in":"query","description":"Page size, 1 to 100. Defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","description":"The `nextCursor` from the previous page. Cursors are opaque and positional rather than offsets: do not construct one, and do not assume two pages taken minutes apart line up.","schema":{"type":"string"}},{"name":"kind","in":"query","description":"Only this splitter.","schema":{"type":"string","enum":["POOL_FEES","LIQUIDITY_FEES"]}},{"name":"asset","in":"query","description":"Only this asset.","schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"200":{"description":"A page of distributions.","content":{"application/json":{"schema":{"type":"object","required":["data","nextCursor"],"properties":{"data":{"type":"array","items":{"type":"object","required":["id","kind","splitter","asset","toJarBaseUnits","toStakersBaseUnits","toCompanyBaseUnits","toBackersBaseUnits"],"properties":{"id":{"type":"string"},"kind":{"type":"string"},"splitter":{"$ref":"#/components/schemas/EvmAddress"},"asset":{"$ref":"#/components/schemas/EvmAddress"},"toJarBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"toStakersBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"toCompanyBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"toBackersBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"txHash":{"type":"string"},"blockNumber":{"$ref":"#/components/schemas/BlockNumber"},"occurredAt":{"$ref":"#/components/schemas/Timestamp"}}}},"nextCursor":{"type":["string","null"]}}}}}},"400":{"description":"`INVALID_REQUEST`: an unknown splitter or a malformed address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/rewards/{address}":{"get":{"operationId":"getRewardClaims","summary":"What one address is owed by every campaign, with its proofs and what it can claim","description":"Every leaf on record for the address, each with the whole entitlement the leaf states and its proof, and — read from the chain at one block — what has vested, what the address has claimed, and what `claimable(id, account, amount)` says a claim pays now. An address owed nothing is an empty list, not a 404, and costs no chain read. A proof is withheld (null, with `proofProblem`) unless it verifies against the campaign's root and the chain still holds that root, so a proof served here is one `claim(id, amount, proof)` accepts; the contract pays the sender, so only this address can use it. The live figures are null when the chain could not be read, and are shared for fifteen seconds. At most the 200 newest campaigns, newest first.","tags":["Rewards"],"parameters":[{"name":"address","in":"path","required":true,"description":"Any EVM address, in any case.","schema":{"$ref":"#/components/schemas/EvmAddress"}}],"responses":{"200":{"description":"The address's claims.","content":{"application/json":{"schema":{"type":"object","required":["address","contract","chainId","chain","claims"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"contract":{"$ref":"#/components/schemas/EvmAddress"},"chainId":{"type":"integer"},"chain":{"type":["object","null"],"properties":{"blockNumber":{"type":"string"},"at":{"$ref":"#/components/schemas/Timestamp"},"paused":{"type":"boolean"}}},"claims":{"type":"array","items":{"type":"object","required":["campaignId","kind","title","startsAt","endsAt","vestForSeconds","status","amountBaseUnits","vestedBaseUnits","claimedBaseUnits","claimableBaseUnits","proof","proofProblem"],"properties":{"campaignId":{"type":"integer"},"kind":{"type":"string","enum":["SEASON","EVENT","FUNDED"]},"tranche":{"type":["string","null"]},"title":{"type":"string"},"startsAt":{"$ref":"#/components/schemas/Timestamp"},"endsAt":{"$ref":"#/components/schemas/Timestamp"},"vestForSeconds":{"type":"integer"},"status":{"type":"string","enum":["UPCOMING","OPEN","ENDED","CLOSED","CANCELLED"]},"amountBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"The whole entitlement: the `amount` the leaf and `claim` take."},"vestedBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"null"}]},"claimedBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"null"}]},"claimableBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"null"}]},"proof":{"type":["array","null"],"items":{"$ref":"#/components/schemas/Hash32"},"description":"Sibling hashes, root-ward last."},"proofProblem":{"oneOf":[{"type":"string","enum":["BAD_PROOF","ROOT_MISMATCH"]},{"type":"null"}],"description":"Why the proof is withheld; null when it is served."}}}}}}}}},"400":{"description":"`INVALID_REQUEST`: not an address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: `COMMUNITY_REWARDS_ADDRESS` is not set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/rewards/campaigns":{"get":{"operationId":"listRewardCampaigns","summary":"Every community campaign this deployment serves, with the chain's live status","description":"One row per `CommunityRewards` campaign whose claims are on record, the 200 newest first: its budget, claim window, vesting, root and `dataHash`, and where its file is. A row is written only after the campaign was read back from the chain and matched its published file. `onChain` is read at one block and shared for a minute — what has been claimed, and whether it is closed or was cancelled — and is null when the chain could not be read, which is not a statement that nothing was claimed. `status` compares the window with the block's own clock, `endsAt` included as the contract does. An event's file is served at `/v1/rewards/files/{dataHash}` (`fileHosted`); a season's is published at `fileUrl`. 503 NOT_CONFIGURED when no contract address is set.","tags":["Rewards"],"responses":{"200":{"description":"The campaigns, and the block their live figures were read at.","content":{"application/json":{"schema":{"type":"object","required":["contract","chainId","chain","campaigns"],"properties":{"contract":{"$ref":"#/components/schemas/EvmAddress"},"chainId":{"type":"integer"},"chain":{"type":["object","null"],"description":"The block read, and the pause, which stops every claim at once.","properties":{"blockNumber":{"type":"string"},"at":{"$ref":"#/components/schemas/Timestamp"},"paused":{"type":"boolean"}}},"campaigns":{"type":"array","items":{"type":"object","required":["campaignId","kind","title","root","dataHash","budgetBaseUnits","startsAt","endsAt","vestForSeconds","status","claimants","onChain"],"properties":{"campaignId":{"type":"integer","description":"The contract's id for it."},"kind":{"type":"string","enum":["SEASON","EVENT","FUNDED"]},"tranche":{"type":["string","null"],"description":"A season's `IMMEDIATE` or `VESTING` half; null otherwise."},"title":{"type":"string"},"root":{"$ref":"#/components/schemas/Hash32"},"dataHash":{"allOf":[{"$ref":"#/components/schemas/Hash32"}],"description":"keccak256 of the published file's exact bytes."},"budgetBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"startsAt":{"$ref":"#/components/schemas/Timestamp"},"endsAt":{"$ref":"#/components/schemas/Timestamp"},"vestForSeconds":{"type":"integer","description":"0 pays each entitlement when claims open."},"status":{"type":"string","enum":["UPCOMING","OPEN","ENDED","CLOSED","CANCELLED"]},"claimants":{"type":"integer","description":"Leaves on record."},"fileUrl":{"type":["string","null"]},"fileHosted":{"type":"boolean"},"importedAt":{"$ref":"#/components/schemas/Timestamp"},"onChain":{"type":["object","null"],"properties":{"claimedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"closed":{"type":"boolean"},"cancelled":{"type":"boolean"},"matches":{"type":"boolean","description":"The chain's root and dataHash equal the row's."}}}}}}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: `COMMUNITY_REWARDS_ADDRESS` is not set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/rewards/files/{dataHash}":{"get":{"operationId":"getRewardEventFile","summary":"An event's published file, byte for byte","description":"The exact bytes whose keccak256 is the event's `dataHash` on chain: its title, window, budget, root and every entry. Rebuild the root with OpenZeppelin's `StandardMerkleTree.of(entries, [\"address\", \"uint256\"])` and compare it with `campaign(id).root`. Served only for an event the events key has created; a draft never sent is not public.","tags":["Rewards"],"parameters":[{"name":"dataHash","in":"path","required":true,"description":"The campaign's `dataHash`.","schema":{"$ref":"#/components/schemas/Hash32"}}],"responses":{"200":{"description":"The file.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"`INVALID_REQUEST`: not a 32-byte hash.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`NOT_FOUND`: no created event has this file.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: `COMMUNITY_REWARDS_ADDRESS` is not set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/rpc":{"post":{"operationId":"readChain","summary":"Read the chain, for a page whose wallet is a phone","description":"A JSON-RPC 2.0 endpoint that forwards reads to this deployment's RPC. A phone wallet connected over WalletConnect is asked only to sign, so the web app sends its `eth_call`s and receipt lookups here instead. Reads only, from a fixed list: nothing that sends, and not `eth_getLogs`. One request per call; a batch is refused. A method not on the list, or a deployment with no RPC, answers with a JSON-RPC error rather than an HTTP one.","tags":["Chain"],"requestBody":{"description":"One JSON-RPC 2.0 request, at most 32 KiB.","required":true,"content":{"application/json":{"schema":{"type":"object","required":["jsonrpc","id","method"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"type":["string","integer","null"]},"method":{"type":"string","enum":["eth_blockNumber","eth_call","eth_chainId","eth_estimateGas","eth_feeHistory","eth_gasPrice","eth_getBalance","eth_getBlockByNumber","eth_getCode","eth_getTransactionByHash","eth_getTransactionCount","eth_getTransactionReceipt","eth_maxPriorityFeePerGas","net_version"]},"params":{"type":"array","items":{},"maxItems":8}}}}}},"responses":{"200":{"description":"The RPC's answer, with the caller's id; or a JSON-RPC error.","content":{"application/json":{"schema":{"type":"object","required":["jsonrpc","id"],"additionalProperties":true}}}},"400":{"description":"A batch, or not a JSON-RPC 2.0 request. A body that is not JSON, or is empty under a JSON content type, is `INVALID_REQUEST` too.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","required":["jsonrpc","id","error"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"type":["string","integer","null"]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer"},"message":{"type":"string"}}}}}]}}}},"413":{"description":"`BODY_TOO_LARGE`: the body is over the limit (1 MiB unless the route states its own) and was not read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`UNSUPPORTED_MEDIA_TYPE`: the content type is one no parser takes. Send `application/json`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The RPC could not be reached, or answered with something that is not JSON-RPC.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","required":["jsonrpc","id","error"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"type":["string","integer","null"]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer"},"message":{"type":"string"}}}}}]}}}}},"x-precrime-described":true}},"/v1/search":{"get":{"operationId":"search","summary":"Search cases and addresses","description":"Three outcomes are reported distinctly and never collapsed: `QUERY_TOO_VAGUE` (nothing was searched for), `NO_MATCH` (the query ran and matched nothing) and `MATCHED`. Collapsing the first two into \"no results\" would tell somebody their subject does not exist when in fact nothing was looked for. `interpretation` says how the query was read, because a search that silently reinterprets what was typed cannot be corrected by the person who typed it.","tags":["Search"],"parameters":[{"name":"q","in":"query","description":"The query. Supports `key:value` filters and free text; an address or case id is recognised by shape.","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size, 1 to 50. Defaults to 20.","schema":{"type":"integer","minimum":1,"maximum":50}}],"responses":{"200":{"description":"The outcome, the interpretation, and any hits.","content":{"application/json":{"schema":{"type":"object","required":["status","interpretation","ambiguous","results","counts"],"properties":{"status":{"type":"string","enum":["QUERY_TOO_VAGUE","MATCHED","NO_MATCH"],"description":"Which of the three outcomes this is. Branch on it, not on `results`."},"interpretation":{"type":"string","description":"How the query was read, in words, so a misreading is visible."},"ambiguous":{"type":"boolean","description":"True when a token had more than one plausible reading. A client should show the alternatives rather than pick one."},"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchHit"}},"counts":{"type":"object","required":["cases","addresses"],"description":"Hits by kind. Both are zero on `QUERY_TOO_VAGUE`, where they are not a measurement of anything — `status` is the field that says so.","properties":{"cases":{"type":"integer"},"addresses":{"type":"integer"}}}}}}}},"400":{"description":"`INVALID_REQUEST`: the query or limit failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/sponsorships/live":{"get":{"operationId":"listLivePlacements","summary":"The paid placements live on one surface now","description":"Only bookings the router has settled are live, each within its booked window. Every placement renders through the booking model, so it always carries the word Sponsored. A surface the product does not render returns none; an editorial surface is refused. Cached for thirty seconds.","tags":["Sponsorships"],"parameters":[{"name":"surface","in":"query","required":true,"description":"A sponsorable surface.","schema":{"type":"string","enum":["DOSSIER_HEADER_BANNER","CASE_DETAIL_ASIDE","MEMESTOCKS_INDEX_BANNER","TELEGRAM_DIGEST_FOOTER","DAILY_BRIEFING_FOOTER"]}}],"responses":{"200":{"description":"The live placements.","content":{"application/json":{"schema":{"type":"object","required":["data","observedAt"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Placement"}},"observedAt":{"$ref":"#/components/schemas/Timestamp"}}}}}},"400":{"description":"`INVALID_REQUEST`: not a sponsorable surface.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/staking":{"get":{"operationId":"getStaking","summary":"The staking vault, live, with its indexed history","description":"Every balance is read from the vault at one block, so the share price and the deposit cap describe the same moment. `yieldRate` is always null and there is no APR anywhere: an APR needs a PRECRIME price and a projection of future funding, and this deployment has neither. What was actually streamed and dripped is in the history instead, which anyone can check against the chain. Each listed dollar is reported in its own units and none are added together. 503 NOT_CONFIGURED when no vault address is set, which is a different statement from an empty vault.","tags":["Staking"],"responses":{"200":{"description":"The vault and what it has done.","content":{"application/json":{"schema":{"type":"object","required":["vault","backstop","stables","yieldRate","history","historySource"],"properties":{"vault":{"type":"object","additionalProperties":true},"backstop":{"type":"object","additionalProperties":true},"stables":{"type":"array","items":{"type":"object","additionalProperties":true}},"yieldRate":{"type":"null","description":"Always null: no price source exists, so no rate can be stated."},"history":{"type":"array","items":{"type":"object","additionalProperties":true}},"historySource":{"type":"string","enum":["INDEXED_CHAIN_EVENTS","NOTHING_INDEXED_YET"]}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`NOT_CONFIGURED`: no vault address is set. `UNAVAILABLE`: the vault could not be read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/suggest":{"get":{"operationId":"suggestCases","summary":"Cases you could open on one address","description":"Everything here is read from the chain at request time and assessed by the same gate that refuses a draft, so a suggestion carries a rule the server has already agreed it could settle, its hash, and a `resolvable` verdict when it could not. A suggestion is never built on something that failed to read: a pool nobody found is absent and its source is named in `coverage`. What a wallet holds, and which Uniswap v4 pools exist, need an adapter this build does not have, so both say NOT_CONFIGURED — naming no setting, since none would turn them on — rather than answering as though the wallet held nothing.","tags":["Cases"],"parameters":[{"name":"address","in":"query","required":true,"description":"The address to look at: a token, a wallet or any other contract.","schema":{"type":"string"}},{"name":"chainId","in":"query","description":"Defaults to Robinhood Chain mainnet (4663).","schema":{"type":"integer"}}],"responses":{"200":{"description":"What this address is, and what could be asked about it.","content":{"application/json":{"schema":{"type":"object","required":["address","chainId","subject","suggestions","coverage"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"chainId":{"type":"integer"},"subject":{"type":"object","required":["kind","detail"],"properties":{"kind":{"type":"string","enum":["TOKEN","WALLET","CONTRACT","UNKNOWN"]},"detail":{"type":"string"}}},"token":{"type":["object","null"],"additionalProperties":true},"pons":{"type":["object","null"],"description":"The curve the Pons factory names for this token, used only after the curve agrees about which token it sells.","additionalProperties":true},"pools":{"type":"array","description":"Pools the venues' own factories name for this token.","items":{"type":"object","additionalProperties":true}},"wallet":{"type":["object","null"],"additionalProperties":true},"suggestions":{"type":"array","items":{"type":"object","required":["template","title","condition","rule","ruleHash","resolvable"],"properties":{"template":{"type":"string"},"title":{"type":"string"},"condition":{"type":"string","description":"The sentence a staker would read, rendered from the rule itself."},"because":{"type":"string","description":"Why this address suggested this case."},"rule":{"type":"object","additionalProperties":true},"ruleHash":{"type":"string"},"resolvable":{"type":"object","additionalProperties":true},"openUrl":{"type":"string"}}}},"coverage":{"type":"array","description":"Every source, what it said, and what it would take to have it.","items":{"type":"object","required":["source","status","detail"],"properties":{"source":{"type":"string"},"status":{"type":"string","enum":["READ","UNAVAILABLE","NOT_CONFIGURED","NOT_APPLICABLE"]},"detail":{"type":"string"}}}}}}}}},"400":{"description":"`INVALID_REQUEST`: the address was not an EVM address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`CHAIN_NOT_CONFIGURED`: no RPC endpoint is configured, so nothing could be read.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/transparency":{"get":{"operationId":"getTransparency","summary":"Protocol accounting","description":"Settlement figures are summed from indexed on-chain `Settled` events, so they are what the chain produced rather than what the application expected. Token allocations are the published genesis manifest, and each block carries a `source` naming which it is: `NO_SETTLEMENTS_YET` means the zeros are genuine rather than missing, and `PUBLISHED_MANIFEST_NOT_YET_DEPLOYED` means the allocation describes an intent rather than a balance. Carry that distinction into whatever you build on top.","tags":["Aggregates"],"responses":{"200":{"description":"Accounting, with the provenance of each block.","content":{"application/json":{"schema":{"type":"object","required":["settlement","cases","feeSchedule","feeScheduleSource","revenue","token"],"properties":{"settlement":{"type":"object","required":["settledCases","voidedCases","byAsset","source"],"description":"Sums over non-voided settlements, one entry per stake asset. There is no total across assets: USDG and PRECRIME count in units twelve orders of magnitude apart, and adding them produces a number in no unit at all.","properties":{"settledCases":{"type":"integer","description":"Settled cases in every asset. A count is the one figure that adds."},"voidedCases":{"type":"integer"},"byAsset":{"type":"array","items":{"type":"object","required":["asset","address","symbol","decimals","burnRoute","settledCases","grossPoolBaseUnits","winnersPoolBaseUnits","treasuryBaseUnits","creatorBaseUnits","burnShareBaseUnits","reserveBaseUnits","referralBaseUnits","unattributedReferralBaseUnits","lockerRebateBaseUnits"],"properties":{"asset":{"type":"string","description":"The asset code."},"address":{"type":["string","null"],"description":"The token contract."},"symbol":{"type":["string","null"],"description":"Null for an asset no published case labels."},"decimals":{"type":["integer","null"],"description":"Null under the same condition; the base units stay exact."},"burnRoute":{"$ref":"#/components/schemas/BurnRoute"},"settledCases":{"type":"integer"},"grossPoolBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"winnersPoolBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"treasuryBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"creatorBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"burnShareBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"reserveBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"referralBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"unattributedReferralBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"lockerRebateBaseUnits":{"$ref":"#/components/schemas/BaseUnits"}}}},"source":{"type":"string","enum":["INDEXED_CHAIN_EVENTS","NO_SETTLEMENTS_YET"],"description":"Where the figures came from, or that there are none yet."}}},"cases":{"type":"object","required":["byStatus","withDeployedPool"],"properties":{"byStatus":{"type":"object","additionalProperties":{"type":"integer"},"description":"Case counts keyed by status. A status with no cases is absent rather than zero, because this is a group-by of what exists."},"withDeployedPool":{"type":"integer","description":"Cases whose pool contract exists on chain."}}},"feeSchedule":{"$ref":"#/components/schemas/FeeSchedule"},"feeScheduleSource":{"type":"string","enum":["CHAIN","FACTORY_CONSTRUCTOR_DEFAULT"],"description":"`CHAIN` when the schedule was read from the factory. `FACTORY_CONSTRUCTOR_DEFAULT` when it could not be: the schedule the factory is constructed with, which the timelock may since have changed. Each case's own schedule is on the case."},"revenue":{"type":"object","required":["source","byAsset","splits","receivers","router"],"description":"Sponsor and API revenue in dollars (decision D-029): what the payment router split, from its own `OrderSettled` legs, one entry per token and never added across them, and the split and receivers the router would use now, read from it.","properties":{"source":{"type":"string","enum":["INDEXED_CHAIN_EVENTS","NO_ORDERS_YET","NOT_CONFIGURED"],"description":"`NO_ORDERS_YET` means the rail is configured and nothing has settled; `NOT_CONFIGURED` means there is no rail to take an order at all."},"byAsset":{"type":"array","items":{"type":"object","required":["asset","address","symbol","decimals","ordersSettled","ordersRefunded","grossBaseUnits","jarBaseUnits","stakerBaseUnits","referralBaseUnits","companyBaseUnits"],"properties":{"asset":{"type":"string","description":"The asset code."},"address":{"$ref":"#/components/schemas/EvmAddress"},"symbol":{"type":["string","null"]},"decimals":{"type":["integer","null"]},"ordersSettled":{"type":"integer"},"ordersRefunded":{"type":"integer"},"grossBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"jarBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"stakerBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"referralBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"companyBaseUnits":{"$ref":"#/components/schemas/BaseUnits"}}}},"splits":{"type":"object","required":["source","sponsor","api"],"properties":{"source":{"type":"string","enum":["CHAIN","UNAVAILABLE","NOT_CONFIGURED"]},"sponsor":{"oneOf":[{"$ref":"#/components/schemas/RevenueSplit"},{"type":"null"}]},"api":{"oneOf":[{"$ref":"#/components/schemas/RevenueSplit"},{"type":"null"}]}}},"receivers":{"oneOf":[{"type":"object","required":["jar","staker","company","jarIsJar","stakerIsVault","companyIsCompanyWallet"],"description":"Where the router sends each leg, and whether each is the contract the deployment named: false means the timelock has moved a receiver elsewhere.","properties":{"jar":{"type":"string"},"staker":{"type":"string"},"company":{"type":"string"},"jarIsJar":{"type":["boolean","null"]},"stakerIsVault":{"type":["boolean","null"]},"companyIsCompanyWallet":{"type":["boolean","null"]}}},{"type":"null"}]},"router":{"oneOf":[{"$ref":"#/components/schemas/EvmAddress"},{"type":"null"}]}}},"offChain":{"type":"object","description":"Revenue that never touches the chain: Telegram Stars paid for Pro (D-054), reported apart from every on-chain figure.","additionalProperties":true},"token":{"type":"object","required":["totalSupplyBaseUnits","totalSupply","allocations","source"],"properties":{"totalSupplyBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"totalSupply":{"$ref":"#/components/schemas/DecimalAmount"},"allocations":{"type":"array","items":{"type":"object","required":["name","baseUnits","amount","sharePct"],"properties":{"name":{"type":"string"},"baseUnits":{"$ref":"#/components/schemas/BaseUnits"},"amount":{"$ref":"#/components/schemas/DecimalAmount"},"sharePct":{"type":"number","description":"Share of supply as a percentage, computed in integer basis points and then divided for display. A derived display figure: `baseUnits` is the authoritative value."}}}},"source":{"type":"string","enum":["PUBLISHED_MANIFEST","PUBLISHED_MANIFEST_NOT_YET_DEPLOYED"],"description":"Whether these figures describe a deployed token or a published intent. They are never read as balances from a chain."}}}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}},"/v1/wake":{"post":{"operationId":"wakeIndexers","summary":"Ask the indexers to look at the chain now","description":"The site sends this after a wallet write confirms, so the stake, lock or payment reaches the product in about a minute rather than up to two. It carries nothing and changes nothing but the indexers' pace, and however often it is called they are woken at most once every ninety seconds. Publishing a case and reporting a sent payment wake them as well.","tags":["Service"],"responses":{"202":{"description":"Accepted. Whether it woke anything is not said: the spacing decides.","content":{"application/json":{"schema":{"type":"object","required":["accepted"],"properties":{"accepted":{"type":"boolean","const":true}}}}}},"429":{"description":"`RATE_LIMITED`: the per-caller window is exhausted. An `X-RateLimit-Policy` header reading `not-configured` means no limit was applied and says why — an unconfigured limit is not an unlimited one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"`INTERNAL_ERROR`. Details are logged and never returned, because they can name internal hosts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-precrime-described":true}}},"components":{"securitySchemes":{"sessionBearer":{"type":"http","scheme":"bearer","bearerFormat":"opaque session token","description":"A session token from `POST /v1/auth/verify`, which is issued against a wallet signature. It acts as the user across the whole product and consumes no metered quota, because it is a person using the product rather than an integration. Key minting and logout are session-only: an API key that could mint another key would let a leaked read-only key escalate itself, and revoking the original would not contain the compromise."},"apiKeyBearer":{"type":"http","scheme":"bearer","bearerFormat":"pc_…","description":"A scoped API key, minted from a session at `POST /v1/private/api-keys` and returned exactly once. Sent in the same `Authorization: Bearer` header as a session token; the server distinguishes the two by the `pc_` prefix. A key holds only the scopes it was granted, can never hold a session-only scope no matter what was written to its record, can never carry an operator permission, and charges every request against its account's daily quota, which answers with `QUOTA_EXCEEDED` and a `resetAt` when spent. Every key of an account shares one per-minute limit at the account's tier, checked first: a request refused `RATE_LIMITED` spends nothing of the day. A key also counts on the public GET reads under `/v1/cases`, `/v1/markets`, `/v1/feed` and `/v1/search`, at its tier's rate and against its day; once the day is spent those are limited per address, as without a key, and never refused for it. There a key the server has not yet found live is looked up only within its address's anonymous limit, and spends one of its requests: a key that does not resolve is answered 401 while the address has room and 429 once it has none."}},"schemas":{"BaseUnits":{"type":"string","pattern":"^\\d+$","description":"An exact integer token amount in the asset's smallest unit — 18 decimals for PRECRIME, 6 for USDG; a case's `stakeAsset.decimals` says which — serialised as a decimal string. It is a string because one PRECRIME at 18 decimals is 10^18 base units, which exceeds the range IEEE-754 doubles hold exactly, so a JSON number would silently round a stake in transit. Parse it with a big-integer type, never with parseFloat, and never add amounts in two assets.","examples":["100000000000000000000000"]},"DecimalAmount":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$","description":"The same amount rendered for display, converted from base units exactly and without rounding. Provided only as a convenience: the `…BaseUnits` field beside it is the authoritative value, and anything that adds, compares or settles should use that.","examples":["100000.5"]},"Bps":{"type":"integer","minimum":0,"maximum":10000,"description":"Basis points, as an integer. 6000 is 60.00%. Rates and implied probabilities are integers for the same reason money is: the contracts compute in integer basis points, and a float here would disagree with the chain in the last decimal place."},"EvmAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"A lower-cased EVM address."},"Hash32":{"type":"string","pattern":"^0x[0-9a-fA-F]{64}$","description":"A 32-byte hex hash."},"RewardEventDraft":{"type":"object","required":["id","status","title","contract","budgetBaseUnits","totalBaseUnits","entryCount","startsAt","endsAt","vestForSeconds","root","dataHash","campaignId","call"],"description":"An EVENT campaign as the console drafted it (decision D-057), and the exact call the events key sends. The server never holds that key.","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["DRAFT","CREATED"]},"title":{"type":"string"},"chainId":{"type":"integer"},"contract":{"$ref":"#/components/schemas/EvmAddress"},"budgetBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"totalBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"What the winners sum to; at most the budget."},"entryCount":{"type":"integer"},"startsAt":{"$ref":"#/components/schemas/Timestamp"},"endsAt":{"$ref":"#/components/schemas/Timestamp"},"vestForSeconds":{"type":"integer"},"root":{"$ref":"#/components/schemas/Hash32"},"dataHash":{"allOf":[{"$ref":"#/components/schemas/Hash32"}],"description":"keccak256 of the draft's file, byte for byte."},"campaignId":{"type":["integer","null"],"description":"The contract's id, once the event was created and read back."},"createdAt":{"$ref":"#/components/schemas/Timestamp"},"call":{"type":"object","required":["to","functionName","args"],"properties":{"to":{"$ref":"#/components/schemas/EvmAddress"},"functionName":{"type":"string","enum":["createEvent"]},"args":{"type":"array","description":"`[root, dataHash, budget, startsAt, endsAt, vestFor]`: the budget in base units as a string, the times in unix seconds.","items":{"type":["string","integer"]}}}}}},"BlockNumber":{"type":["string","null"],"pattern":"^\\d+$","description":"A block number, as a string for the same reason money is a string: it is a 64-bit value off-chain and a JSON number would not survive the top of that range. Null when the event was produced by the application rather than read from a chain."},"Timestamp":{"type":"string","format":"date-time","description":"An ISO-8601 instant in UTC."},"NullableTimestamp":{"type":["string","null"],"format":"date-time","description":"An ISO-8601 instant in UTC, or null when the moment has not happened. Null is never a stand-in for an epoch date."},"Chain":{"type":"string","enum":["ETHEREUM","POLYGON","ARBITRUM","OPTIMISM","BASE","ROBINHOOD_MAINNET","ROBINHOOD_TESTNET","SOLANA_MAINNET","SOLANA_DEVNET","SOLANA_LOCALNET"],"description":"A chain's enum name, which is how tracked addresses are keyed. Cases carry the numeric `chainId` instead, so the same chain is a name on one record and a number on another."},"CaseStatus":{"type":"string","enum":["DRAFT","ACTIVE","PROVISIONAL","MANUAL_REVIEW","SETTLED_GUILTY","SETTLED_INNOCENT","VOID","CANCELLED"],"description":"Mirrors the on-chain pool status. `DRAFT` exists only off-chain, before a pool is deployed, and is never returned by a public read: a list of unpublished ideas belongs to its creator."},"PositionSide":{"type":"string","enum":["GUILTY","INNOCENT"],"description":"Which side of the pool a stake is on. These are game-side labels about a rule outcome, never factual allegations about a person."},"ErrorCode":{"type":"string","enum":["INVALID_REQUEST","UNAUTHORIZED","FORBIDDEN","NOT_FOUND","RATE_LIMITED","QUOTA_EXCEEDED","NOT_CONFIGURED","OPERATOR_AWAY","INTERNAL_ERROR","UNKNOWN_CHALLENGE","CHALLENGE_CONSUMED","EXPIRED","DOMAIN_MISMATCH","ADDRESS_MISMATCH","BAD_SIGNATURE","WALLET_ALREADY_LINKED","LINK_TOKEN_UNKNOWN","LINK_TOKEN_USED","LINK_TOKEN_EXPIRED","TELEGRAM_LINKED_ELSEWHERE","ACCOUNT_LINKED_ELSEWHERE","LINK_RACED","WALLET_SESSION_REQUIRED","OPERATOR_REQUIRES_SESSION","PRIVATE_WEBHOOK_DESTINATION","WEBHOOK_SIGNING_NOT_CONFIGURED","NOT_REPLAYED","CASE_CLOSED_TO_SUBMISSIONS","UNRECOGNISED_CASE_STATUS","UNRECOGNISED_EVIDENCE_KIND","MALFORMED_BASE64","TOO_LARGE","TOO_LARGE_FOR_TYPE","DENIED_CONTENT","UNRECOGNISED_CONTENT","EMPTY_FILE","MALFORMED_OR_TRUNCATED","LOCATION_METADATA_PRESENT","BODY_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","INVALID_CASE_RULE","STAKE_WINDOW_TOO_SHORT","DEADLINE_BEFORE_CUTOFF","CHALLENGE_WINDOW_OUT_OF_RANGE","NOT_RESOLVABLE","WINDOW_REQUIRED","WINDOW_NOT_STAKE_FIRST","WINDOW_BEFORE_CUTOFF","WINDOW_TOO_SHORT","WINDOW_PAST_DEADLINE","DEADLINE_TOO_FAR","CHAIN_NOT_CONFIGURED","CHAIN_UNAVAILABLE","CHAIN_MISMATCH","POOL_NOT_FROM_FACTORY","RULE_HASH_MISMATCH","CREATOR_NOT_VERIFIED","TX_DOES_NOT_CREATE_POOL","ALREADY_PUBLISHED","STAKE_ASSET_NOT_ALLOWED","STAKE_ASSET_MISMATCH","TIMING_MISMATCH","STAKE_ASSET_NOT_SUPPORTED","INVALID_STAKE_ASSET","TERMS_MISMATCH","SETTLER_REQUIRED","SETTLER_REFUSED","INVALID_CONFIRM_TIMEOUT","RULING_NOT_ALLOWED","ESCALATION_CLOSED","NOTHING_TO_ESCALATE","INVALID_REASON","NO_VERDICT_TO_REPORT","PAYMENTS_NOT_CONFIGURED","PAYMENTS_DEGRADED","TOKEN_NOT_ACCEPTED","WALLET_REQUIRED","SPONSOR_NOT_APPROVED","SPONSOR_NOT_IDENTIFIED","CREATIVE_INVALID","INVALID_CREATIVE","SLOT_AT_CAPACITY","SURFACE_NOT_LIVE","PLAN_NOT_PURCHASABLE","PLAN_PAUSED","UNKNOWN_SLOT","INVALID_RANGE","SEND_COUNT_REQUIRED","STARTS_IN_THE_PAST","UNREADABLE_BOOKING","UNUSABLE_INSTANT","TOO_MANY_OPEN_QUOTES","ORDER_NOT_CANCELLABLE","ORDER_CHANGED","UNAVAILABLE"],"description":"Every code the service returns, read from its handlers and its error handler. The seven the narrative reference lists come first. The rest — `RATE_LIMITED`, the sign-in refusals, the operator refusal and the route-specific refusals — are listed even though that page omits them, because they are served: a client that does not handle a code it can receive will treat it as an unknown failure."},"Error":{"type":"object","required":["error"],"description":"The error envelope. `error` is the only field guaranteed present, and it is the field to branch on — the companions below appear only where the service has something specific to add, and their absence is not a value.","properties":{"error":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","description":"Human-readable detail, present on some codes and absent on others. Never parsed for meaning, and never present on `INTERNAL_ERROR`: internal messages can name internal hosts, so they are logged instead of returned."},"issues":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Validation issues, on `INVALID_REQUEST` only. The shape is whatever the request schema reported, field path included."},"requiredScope":{"type":"string","description":"On a `FORBIDDEN` raised by the scope gate: the scope this credential is missing. A key can be re-minted with it; for a session-only scope no key will ever hold it."},"detail":{"type":"string","description":"On the evidence refusals: what was refused and why, in words that also say whether anything was stored."},"resetAt":{"allOf":[{"$ref":"#/components/schemas/Timestamp"}],"description":"On `QUOTA_EXCEEDED`: when the key's daily window resets and requests are accepted again."},"reason":{"type":"string","description":"On `PRIVATE_WEBHOOK_DESTINATION` and `NOT_REPLAYED`: what specifically was refused, so the caller can fix the request rather than retry it unchanged."}}},"Endpoint":{"type":"object","required":["method","path","access"],"description":"One route, as reported by the running service.","properties":{"method":{"type":"string","description":"The HTTP method."},"path":{"type":"string","description":"The path, in Fastify's `:param` form."},"access":{"type":"string","enum":["public","authenticated"],"description":"Whether a credential is required. Derived from the `/v1/private` prefix, which is exactly what the server's authentication hook gates on."}}},"FeeSchedule":{"type":"object","required":["feeBps","treasuryBps","creatorBps","burnBps","reserveBps","referralBps"],"description":"The protocol fee and its split. `feeBps` is the total taken from the gross pool; the five components sum to it. `creatorBps` is owed to whoever opened the case, claimable once it settles.","properties":{"feeBps":{"$ref":"#/components/schemas/Bps"},"treasuryBps":{"$ref":"#/components/schemas/Bps"},"creatorBps":{"$ref":"#/components/schemas/Bps"},"burnBps":{"$ref":"#/components/schemas/Bps"},"reserveBps":{"$ref":"#/components/schemas/Bps"},"referralBps":{"$ref":"#/components/schemas/Bps"}}},"StakeAsset":{"type":"object","required":["address","symbol","decimals"],"description":"What a pool is staked in (decision D-020). Identified by `address`, never by `symbol`: Robinhood Chain carries tokens calling themselves USDC and USDT that are not the real ones. `symbol` and `decimals` are the token's own answers, read from the chain when the case was published, and every amount on the case is formatted with those decimals.","properties":{"address":{"type":"string","pattern":"^0x[0-9a-f]{40}$","description":"The token contract, lower-cased."},"symbol":{"type":"string","description":"A label for display. Never an identity."},"decimals":{"type":"integer","minimum":0,"maximum":18,"description":"6 for USDG, 18 for PRECRIME. The factory lists nothing with more than 18."}}},"BurnRoute":{"type":"string","enum":["BURNED","JAR"],"description":"Where a pool's burn share goes. `BURNED`: PRECRIME destroyed in place, only ever on a PRECRIME pool. `JAR`: the share is sent to the jar, which releases it only against burned PRECRIME — a transfer at settlement, and never to be reported as a burn."},"FactoryStakeAsset":{"type":"object","required":["address","symbol","decimals","allowed","maxGrossPoolBaseUnits","maxOpenExposureBaseUnits","openExposureBaseUnits","burnRoute","burnSink"],"description":"One entry of the factory's stake-asset allowlist, as the chain holds it.","properties":{"address":{"type":"string","pattern":"^0x[0-9a-f]{40}$","description":"The token contract, lower-cased. The asset's identity."},"symbol":{"type":["string","null"],"description":"The token's own `symbol()`, or null when its answer is unreadable or unfit to print. A listed asset without a printable symbol is still listed."},"decimals":{"type":"integer","minimum":0,"maximum":18,"description":"Stored by the factory when the asset was listed."},"allowed":{"type":"boolean","description":"False once disallowed. Pools already built in it keep working to the end."},"maxGrossPoolBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"The base ceiling for a new pool, before the ceiling policy scales it for the creator's bond tier (1×, 2× or 4× at launch)."},"maxOpenExposureBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"The most every open pool in this asset may hold between them."},"openExposureBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"The sum of the ceilings of every open pool in this asset right now. A new pool is refused once its ceiling would take this past `maxOpenExposureBaseUnits`."},"burnRoute":{"$ref":"#/components/schemas/BurnRoute"},"burnSink":{"type":["string","null"],"description":"The jar, on the `JAR` route. Null on `BURNED`, where nothing is sent."}}},"CaseFactoryTerms":{"type":"object","required":["address","chainId","bondToken","feeSchedule","lockerRebateBps","ceilingPolicy","minChallengeWindowSeconds","priceSettler","confirmTimeoutSeconds","stakeAssets","source","readAt"],"description":"What the case factory offers a new case, read from the chain. Every field is a timelock setting, so none of it is configured anywhere else.","properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"chainId":{"type":"integer","description":"The chain the factory was read on."},"bondToken":{"allOf":[{"$ref":"#/components/schemas/EvmAddress"}],"description":"PRECRIME. Creator bonds are posted in it whatever the stake asset."},"feeSchedule":{"$ref":"#/components/schemas/FeeSchedule"},"lockerRebateBps":{"allOf":[{"$ref":"#/components/schemas/Bps"}],"description":"What a new pool rebates to winning lockers, from the treasury's share."},"ceilingPolicy":{"allOf":[{"$ref":"#/components/schemas/EvmAddress"}],"description":"The contract that scales each asset's base ceiling by creator tier."},"minChallengeWindowSeconds":{"type":"integer","description":"`minChallengeWindow()`: the shortest challenge window a new case may choose (decision D-083), a timelock setting from 900 to 3,600 seconds, 3,600 at launch. A lightning draft's challenge window is exactly this (decision D-084)."},"priceSettler":{"allOf":[{"$ref":"#/components/schemas/EvmAddress"}],"description":"`priceSettler()` (D-086): the contract a new crypto or dollar price case binds its terms at (`createPriceCase`), and the settler its rule names (`settlement.settler`, rule `CHAIN_PROOF_V1`). A timelock setting; existing pools keep theirs."},"confirmTimeoutSeconds":{"type":"integer","minimum":3600,"maximum":259200,"description":"`confirmTimeout()` (D-086): how long past the moment claims would have opened a new pool's verifier may still confirm its verdict; from then an unconfirmed verdict voids. 86,400 at launch, a timelock setting from 3,600 to 259,200."},"stakeAssets":{"type":"array","items":{"$ref":"#/components/schemas/FactoryStakeAsset"},"description":"Every asset ever listed, in listing order, allowed or not."},"source":{"type":"string","const":"CHAIN"},"readAt":{"allOf":[{"$ref":"#/components/schemas/Timestamp"}],"description":"When the chain was read. The API holds a read for up to a minute."}}},"CaseRule":{"type":"object","additionalProperties":true,"description":"The canonical rule JSON that `rule.hash` commits to. Its shape depends on the case type; `GET /v1/case-templates` lists the types. Recompute the keccak256 of this object and you can prove the deployed pool settles against the rule that was published. Every new rule is `version: 3` and states its `window` (decision D-067): `from` is `STAKE_CUTOFF` — only what happens after staking closes counts, from the state when it closed — and `until` is `{ at }`, unix seconds, where counting stops. Rules published at `version: 2` carry no window and keep D-030's: from the pool's creation to the stake cutoff."},"Case":{"type":"object","required":["id","title","caseType","caseTypeLabel","status","chainId","poolAddress","stakeAsset","stakeAssetAddress","burnRoute","maxGrossPoolBaseUnits","target","rule","fee","lockerRebateBps","timeline","verdict","sideLabels","openedByOwner","lightning"],"description":"A case as every endpoint serialises it. Never carries internal ids or a creator.","properties":{"id":{"type":"string","description":"The public case id. Twelve lowercase hex characters."},"title":{"type":"string","description":"The case title as published."},"caseType":{"type":"string","description":"The resolution template this case is built on."},"caseTypeLabel":{"type":"string","description":"The human label for `caseType`. Falls back to the raw enum name rather than to an empty string when a build has no label for a type the database holds."},"sideLabels":{"$ref":"#/components/schemas/SideLabels"},"openedByOwner":{"type":"boolean","description":"An account market opened by the owner of the Hyperliquid account it is about (decision D-082), shown on every surface as \"Opened by the account's owner\". Read from the watch written when the case was published, and permanent once set. False for every other type."},"status":{"$ref":"#/components/schemas/CaseStatus"},"lightning":{"type":"boolean","description":"A lightning round (decision D-084): set once, at publish, from the pool's own times read back from the chain — staking ran at most 30 minutes from the block the pool was created in, counting runs at most 30 after its cutoff, its challenge window is at most an hour, and its deadline is under a day after counting stops, which only a draft held to the lightning table and preset may have. No client ever sends it. False on a draft, whose pool does not exist yet."},"chainId":{"type":"integer","description":"The chain the pool is deployed on."},"poolAddress":{"type":["string","null"],"pattern":"^0x[0-9a-fA-F]{40}$","description":"Null until the pool contract exists. A draft has no address."},"stakeAsset":{"oneOf":[{"$ref":"#/components/schemas/StakeAsset"},{"type":"null"}],"description":"What the pool is staked in, with the scale every amount on this case is read at. Null on a draft, whose pool does not exist yet to be asked."},"stakeAssetAddress":{"type":["string","null"],"pattern":"^0x[0-9a-f]{40}$","description":"The asset's address alone: the one a draft chose before publish, the pool's after. Null on a draft that left the choice to the deploy step."},"burnRoute":{"oneOf":[{"$ref":"#/components/schemas/BurnRoute"},{"type":"null"}],"description":"Where this pool's burn share goes. Null on a draft."},"maxGrossPoolBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"null"}],"description":"The pool's immutable ceiling, read at publish. Null on a draft."},"target":{"type":"object","required":["address","tokenAddress"],"description":"What the rule observes. Being a target is not an allegation: it is the subject of a time-bounded forecast about public data.","properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"tokenAddress":{"type":["string","null"],"pattern":"^0x[0-9a-fA-F]{40}$","description":"The token the rule concerns, where the template needs one."}}},"rule":{"type":"object","required":["hash","json","condition"],"description":"The published rule and its commitment. `meaning` and `source` are absent — not null — on list responses, because the narrative is only computed for the dossier read.","properties":{"hash":{"$ref":"#/components/schemas/Hash32"},"json":{"$ref":"#/components/schemas/CaseRule"},"condition":{"type":"string","description":"The resolvable condition in one sentence."},"meaning":{"type":"string","description":"What a GUILTY verdict does and does not assert. Present on the dossier read only."},"source":{"type":"string","description":"Which data settles the case, and from what finality. Present on the dossier read only."}}},"fee":{"$ref":"#/components/schemas/FeeSchedule"},"lockerRebateBps":{"allOf":[{"$ref":"#/components/schemas/Bps"}],"description":"Bps of gross this pool rebates to winning lockers, carved from the treasury's share (decision D-026). Zero on a case whose pool pays none."},"timeline":{"type":"object","required":["createdAt","openedAt","stakeCutoffAt","resolutionDeadlineAt","challengeWindowSeconds","claimsOpenAt","resolvedAt","reviewTimeoutSeconds","challengedAt","reviewVoidableAt"],"description":"Every instant is null until it has actually happened.","properties":{"createdAt":{"$ref":"#/components/schemas/Timestamp"},"openedAt":{"$ref":"#/components/schemas/NullableTimestamp"},"stakeCutoffAt":{"$ref":"#/components/schemas/NullableTimestamp"},"resolutionDeadlineAt":{"$ref":"#/components/schemas/NullableTimestamp"},"challengeWindowSeconds":{"type":["integer","null"],"description":"How long a provisional verdict can be challenged. Null on a draft."},"claimsOpenAt":{"$ref":"#/components/schemas/NullableTimestamp"},"resolvedAt":{"$ref":"#/components/schemas/NullableTimestamp"},"reviewTimeoutSeconds":{"type":["integer","null"],"minimum":86400,"maximum":2592000,"description":"The pool's `reviewTimeout()`, read at publish: how long a review may wait for a ruling before anyone may void the case (G-002). 3 days at launch (D-085); the timelock may set 1 day to 30 days for new pools. Null on a draft."},"challengedAt":{"type":["string","null"],"format":"date-time","description":"When a challenge from before D-086 froze the verdict. A D-086 review's opening is `review.openedAt`. Null otherwise."},"reviewVoidableAt":{"type":["string","null"],"format":"date-time","description":"A challenge's `challengedAt + reviewTimeoutSeconds`: from this instant the pool refuses `resolveManualReview`, and anyone may call its `voidStaleReview()`, which refunds every stake. A D-086 review's is `review.timesOutAt`."}}},"verdict":{"type":["object","null"],"required":["side","final"],"description":"Null until a verdict is posted. A verdict with `final: false` is provisional and can still be overturned inside the challenge window; presenting it as settled is the one misreading this field exists to prevent.","properties":{"side":{"type":"string","enum":["GUILTY","INNOCENT","UNRESOLVED"],"description":"The side the rule resolved to."},"final":{"type":"boolean","description":"True only once the case status itself is settled."}}},"confirmTimeoutSeconds":{"type":["integer","null"],"description":"The pool's `confirmTimeout()`, read at publish (D-086): the verifier's time to confirm past the moment claims would have opened. Null on a draft."},"verification":{"type":["object","null"],"description":"Where the verdict stands with the verifier (D-086); null before one is posted. `state`: `AWAITING` (it may still answer, until `confirmDueAt`), `CONFIRMED` (claims open at the later of the window's end and `confirmedAt`), `DISPUTED` (a review opened), `PROVEN` (a price case's verdict, the settler's own round proof) or `TIMED_OUT` (unconfirmed by `confirmDueAt`: the verdict voids and everyone is refunded). `verifierVerdict` is `NONE` for an unmeasurable reading.","properties":{"state":{"type":"string","enum":["AWAITING","CONFIRMED","DISPUTED","PROVEN","TIMED_OUT"]},"verifier":{"type":["string","null"]},"confirmedAt":{"$ref":"#/components/schemas/NullableTimestamp"},"confirmDueAt":{"$ref":"#/components/schemas/NullableTimestamp"},"verifierVerdict":{"type":["string","null"],"description":"`GUILTY`, `INNOCENT` or `NONE`; null until the verifier answers."}}},"review":{"type":["object","null"],"description":"The review the verdict is or was in (D-086); null when there never was one. `cause` is `DISAGREEMENT` (the verifier read it differently), `ESCALATION` (the operator, with a public reason) or `CHALLENGE` (before D-086). `reasonText` is the escalation's words only when they hash to `reasonDigest`, the digest the pool stored. `allowedRulings` is what the record alone proves the pool accepts: a refund, the verifier's side, and a price case's proven side.","properties":{"cause":{"type":"string","enum":["CHALLENGE","DISAGREEMENT","ESCALATION"]},"reasonText":{"type":["string","null"]},"reasonDigest":{"type":["string","null"]},"openedAt":{"$ref":"#/components/schemas/NullableTimestamp"},"timesOutAt":{"$ref":"#/components/schemas/NullableTimestamp"},"allowedRulings":{"type":"array","items":{"type":"string","enum":["NONE","GUILTY","INNOCENT"]}}}},"proof":{"type":["object","null"],"description":"A crypto or dollar price case's on-chain settlement (D-086, rule `CHAIN_PROOF_V1`); null for any other. `terms` are what the settler bound in the pool's creation transaction; `baselineRound` and `baselineAnswer` the round in effect when staking closed; `round`, `answer` and `updatedAt` the round that decided it, null (never zero) when the outcome names none. Round ids and answers are decimal strings.","additionalProperties":true}}},"CasePool":{"type":"object","required":["guiltyBaseUnits","innocentBaseUnits","grossPoolBaseUnits","guilty","innocent","guiltyImpliedBps","innocentImpliedBps","projectedWinnersPoolBaseUnits"],"description":"Live parimutuel state, computed from recorded stakes.","properties":{"guiltyBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"innocentBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"grossPoolBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"guilty":{"$ref":"#/components/schemas/DecimalAmount"},"innocent":{"$ref":"#/components/schemas/DecimalAmount"},"guiltyImpliedBps":{"$ref":"#/components/schemas/Bps"},"innocentImpliedBps":{"$ref":"#/components/schemas/Bps"},"projectedWinnersPoolBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"What the winning side would share at the current pool size, after the fee. A projection from present state, not a promise: it moves with every further stake."}}},"CaseSettlement":{"type":["object","null"],"required":["verdict","voided","grossPoolBaseUnits","winnersPoolBaseUnits","treasuryBaseUnits","creatorBaseUnits","burnShareBaseUnits","burnRoute","reserveBaseUnits","referralBaseUnits","unattributedReferralBaseUnits","lockerRebateBaseUnits","asset","txHash","settledAt"],"description":"The settled split, taken from the indexed on-chain `Settled` event, every amount in the case's stake asset. Null until the case settles — which is different from a settlement of zero.","properties":{"verdict":{"type":"string","enum":["GUILTY","INNOCENT","UNRESOLVED"],"description":"The final verdict the pool settled on."},"voided":{"type":"boolean","description":"A voided case refunds every stake and takes no fee."},"asset":{"type":"string","pattern":"^erc20:\\d+:0x[0-9a-f]{40}$","description":"The asset code every amount here is counted in: `erc20:<chainId>:<address>`."},"grossPoolBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"winnersPoolBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"treasuryBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"creatorBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"burnShareBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"Burned in place when `burnRoute` is `BURNED`; sent to the jar when `JAR`."},"burnRoute":{"$ref":"#/components/schemas/BurnRoute"},"reserveBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"referralBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"Referral owed to referrers, each of whom claims their own."},"unattributedReferralBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"Referral nobody was owed, paid with the treasury's share."},"lockerRebateBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"What winning lockers are owed between them. Part of `treasuryBaseUnits`, not in addition to it: the rebate is carved from the treasury's component."},"txHash":{"type":["string","null"],"description":"The settling transaction."},"settledAt":{"$ref":"#/components/schemas/Timestamp"}}},"CaseEvidence":{"type":"object","required":["kind","uri","contentHash","occurredAt","submittedAt"],"description":"One piece of submitted evidence. Content-addressed, so a swap is detectable.","properties":{"kind":{"type":"string","description":"What kind of artefact this is."},"uri":{"type":["string","null"],"description":"Where the artefact lives, when it lives somewhere addressable."},"contentHash":{"type":"string","description":"Hash of the submitted content. Unique per case."},"occurredAt":{"$ref":"#/components/schemas/NullableTimestamp"},"submittedAt":{"$ref":"#/components/schemas/Timestamp"}}},"CaseEvent":{"type":"object","required":["type","payload","txHash","blockNumber","occurredAt"],"description":"One entry of the case's history, in occurrence order. Chain-derived entries carry a transaction; application-derived ones do not.","properties":{"type":{"type":"string","description":"The event type."},"payload":{"type":"object","additionalProperties":true,"description":"The event's own fields. Shape depends on `type`."},"txHash":{"type":["string","null"],"description":"Null for an application-derived event."},"blockNumber":{"$ref":"#/components/schemas/BlockNumber"},"occurredAt":{"$ref":"#/components/schemas/Timestamp"}}},"CaseDossier":{"allOf":[{"$ref":"#/components/schemas/Case"}],"description":"A single case with everything the public record holds: the pool, the settlement if there is one, the evidence and the full event history. `ruleVerified` is the hash check performed server-side on every read.","type":"object","required":["ruleVerified","pool","settlement","evidence","events"],"properties":{"ruleVerified":{"type":"boolean","description":"Whether the published rule JSON still hashes to `rule.hash`. False means the prose and the on-chain commitment disagree, and nothing on the page should be trusted until that is explained. It is re-checked per request rather than cached."},"pool":{"$ref":"#/components/schemas/CasePool"},"settlement":{"$ref":"#/components/schemas/CaseSettlement"},"evidence":{"type":"array","items":{"$ref":"#/components/schemas/CaseEvidence"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/CaseEvent"}},"price":{"$ref":"#/components/schemas/CasePrice"},"account":{"$ref":"#/components/schemas/CaseAccount"}}},"Position":{"type":"object","required":["caseId","caseTitle","caseStatus","side","asset","assetSymbol","stakeBaseUnits","stake","lockerStakeBaseUnits","entryImpliedProbabilityBps","payoutBaseUnits","payout","rebateBaseUnits","refunded","settledAt"],"description":"A participant's accumulated stake on one side of one case, from wallets linked to this account. Every amount is in the case's stake asset.","properties":{"caseId":{"type":"string","description":"The public case id."},"caseTitle":{"type":"string","description":"The case title, so a list needs no second call."},"caseStatus":{"$ref":"#/components/schemas/CaseStatus"},"side":{"$ref":"#/components/schemas/PositionSide"},"asset":{"type":"string","description":"The asset code the amounts are counted in: `erc20:<chainId>:<address>`."},"assetSymbol":{"type":["string","null"],"description":"The case's stake asset symbol, for display. Null only on an unscalable row."},"stakeBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"stake":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}],"description":"Formatted with the case's own decimals. Null for a row the product cannot scale, rather than formatted at a guess."},"lockerStakeBaseUnits":{"allOf":[{"$ref":"#/components/schemas/BaseUnits"}],"description":"The part of the stake that earns the locker rebate (decision D-026)."},"entryImpliedProbabilityBps":{"type":["integer","null"],"minimum":0,"maximum":10000,"description":"The pool's implied probability at the first stake, snapshotted so a call can be scored against the odds it was actually made at. Null when no snapshot was taken — which is not the same as 50.00%, and is why this is nullable rather than defaulted."},"payoutBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"null"}],"description":"What the pool paid on this position's claims, summed over the account's linked wallets. Null until a claim is indexed, which is not a payout of zero."},"payout":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}],"description":"`payoutBaseUnits` formatted with the case's decimals."},"rebateBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"null"}],"description":"The locker rebate paid with the payout. Null until one is indexed."},"refunded":{"type":"boolean","description":"True once a refund from a voided case has been claimed."},"settledAt":{"allOf":[{"$ref":"#/components/schemas/NullableTimestamp"}],"description":"When the case reached a terminal state, settled or voided."}}},"Reputation":{"type":"object","required":["score","rank","rankLabel","accuracyPct","callsSettled","callsCorrect","falseVisions","currentStreak","bestStreak"],"description":"Crime Score and its components. Measures whether someone was right, how early, and how far against the consensus — never profit and never stake size.","properties":{"score":{"type":"integer","description":"Crime Score."},"rank":{"type":"integer","description":"Rank tier index."},"rankLabel":{"type":"string","description":"The tier's name."},"accuracyPct":{"type":"number","description":"Correct calls as a percentage of settled calls. A display figure."},"callsSettled":{"type":"integer","description":"Calls on cases that have settled."},"callsCorrect":{"type":"integer","description":"Of those, the ones that were right."},"falseVisions":{"type":"integer","description":"Settled calls that were wrong."},"currentStreak":{"type":"integer","description":"Consecutive correct calls, most recent run."},"bestStreak":{"type":"integer","description":"Longest run of correct calls."}}},"RankTier":{"type":"object","required":["rank","label","minScore"],"description":"One rank threshold, served so a client cannot drift from the server's tiers.","properties":{"rank":{"type":"integer","description":"Tier index."},"label":{"type":"string","description":"Tier name."},"minScore":{"type":"integer","description":"Lowest Crime Score in this tier."}}},"ApiKeySummary":{"type":"object","required":["id","name","prefix","scopes","quotaLimit","createdAt"],"description":"A key's metadata. The secret is not here and cannot be retrieved.","properties":{"id":{"type":"string","description":"The key's id, for revocation."},"name":{"type":"string","description":"The name given at creation."},"prefix":{"type":"string","description":"The visible leading characters, so a key can be identified without the secret."},"scopes":{"type":"array","items":{"type":"string"},"description":"What this key may do. Unknown and session-only scopes are dropped at creation, so this is what was actually granted rather than what was asked for."},"quotaLimit":{"type":"integer","description":"Requests allowed per daily window, set from the owner's plan at minting."},"requestsUsed":{"type":"integer","description":"Requests charged in the current window."},"lastUsedAt":{"$ref":"#/components/schemas/NullableTimestamp"},"createdAt":{"$ref":"#/components/schemas/Timestamp"}}},"WebhookEndpoint":{"type":"object","required":["id","url","events","status","createdAt"],"description":"A delivery destination. The signing secret is returned once, at creation.","properties":{"id":{"type":"string","description":"The endpoint's id, for disabling."},"url":{"type":"string","format":"uri","description":"Where deliveries are POSTed."},"events":{"type":"array","items":{"type":"string"},"description":"The event types this endpoint is subscribed to."},"status":{"type":"string","enum":["ACTIVE","DISABLED"],"description":"Deleting an endpoint disables it rather than removing the record."},"failureCount":{"type":"integer","description":"Consecutive delivery failures, on the list read."},"createdAt":{"$ref":"#/components/schemas/Timestamp"}}},"MarketSummary":{"type":"object","required":["id","question","outcomes","active","closesAt","slug","url"],"description":"One upstream Polymarket market, read-only. No price or odds is included.","properties":{"id":{"type":"string","description":"The upstream market id."},"question":{"type":"string","description":"The market question, as upstream states it."},"outcomes":{"type":"array","items":{"type":"string"},"description":"The outcome labels. Gamma sends them as a JSON-encoded string; they are decoded here, and an undecodable value is an empty list rather than a guess."},"active":{"type":"boolean","description":"Whether upstream reports the market as active."},"closesAt":{"$ref":"#/components/schemas/NullableTimestamp"},"slug":{"type":["string","null"],"description":"The market's slug on Polymarket, or null when upstream sent none."},"url":{"type":["string","null"],"format":"uri","description":"`https://polymarket.com/market/{slug}`, which Polymarket documents and which redirects to the market's event page; null without a slug."}}},"PolymarketEvent":{"type":"object","required":["slug","title","url","endsAt","volumeUsd","markets","openMarkets"],"description":"One open Polymarket event, read-only: what it asks, when it ends, how much has traded on it and where to see it. Every field is Polymarket's own, restated. No price, no odds: those stay withheld until the feed's display terms are confirmed.","properties":{"slug":{"type":"string","description":"The event's slug on Polymarket."},"title":{"type":"string","description":"The event's title, as Polymarket states it."},"url":{"type":"string","format":"uri","description":"`https://polymarket.com/event/{slug}`, the page Polymarket documents for it."},"endsAt":{"$ref":"#/components/schemas/NullableTimestamp"},"volumeUsd":{"type":["string","null"],"pattern":"^\\d+$","description":"Polymarket's all-time volume for the event in whole dollars, rounded down, as a decimal string. Polymarket's figure, not preCrime's; null when Polymarket gave none."},"markets":{"type":"array","maxItems":5,"description":"Up to five of the event's open markets, in Polymarket's order. A settled market inside an open event is never listed.","items":{"type":"object","required":["question","outcomes"],"properties":{"question":{"type":"string"},"outcomes":{"type":"array","items":{"type":"string"},"description":"Outcome labels."}}}},"openMarkets":{"type":"integer","minimum":1,"description":"How many of the event's markets are open, including those not listed."}}},"SearchCaseHit":{"type":"object","required":["kind","id","title","caseType","sideLabels","openedByOwner","status","lightning","chainId","matchedOn"],"description":"A case that matched.","properties":{"kind":{"type":"string","const":"case"},"id":{"type":"string","description":"The public case id."},"title":{"type":"string"},"caseType":{"type":"string","description":"The resolution template's enum name."},"sideLabels":{"$ref":"#/components/schemas/SideLabels"},"openedByOwner":{"type":"boolean","description":"An account market opened by the owner of the Hyperliquid account it is about (D-082), from the watch written when it was published. False for every other type."},"status":{"$ref":"#/components/schemas/CaseStatus"},"lightning":{"type":"boolean","description":"A lightning round (D-084): the server's reading of the pool's own times at publish."},"chainId":{"type":"integer"},"matchedOn":{"type":"string","description":"Which term matched — `target address`, `case id`, `title`, `case type` or `filters` — so the row can explain why it is here. A search that cannot explain itself cannot be corrected by the person who typed the query."}}},"SearchAddressHit":{"type":"object","required":["kind","address","chain","displayName","matchedOn"],"description":"A tracked address that matched. Returned only when the query contained an address, so a wallet with no cases still comes back as itself rather than as nothing at all.","properties":{"kind":{"type":"string","const":"address"},"address":{"$ref":"#/components/schemas/EvmAddress"},"chain":{"allOf":[{"$ref":"#/components/schemas/Chain"}],"description":"A tracked address is keyed by chain name rather than numeric id, so this is a name where a case hit carries a number."},"displayName":{"type":["string","null"],"description":"The watcher's own label, when they set one."},"matchedOn":{"type":"string","const":"address"}}},"SearchHit":{"oneOf":[{"$ref":"#/components/schemas/SearchAddressHit"},{"$ref":"#/components/schemas/SearchCaseHit"}],"description":"Branch on `kind`. Address hits precede case hits in `results`."},"EvidenceKind":{"type":"string","enum":["CHAIN_TRANSACTION","CHAIN_LOG","CHAIN_STATE_READING","ONCHAIN_PRICE_READING","CONGRESSIONAL_DISCLOSURE_FILING","SEC_INSIDER_FILING","OFFICIAL_RECORD_DOCUMENT","SOCIAL_POST","NEWS_ARTICLE","SOCIAL_ATTENTION_METRIC","THIRD_PARTY_ANALYTICS","OFFCHAIN_PRICE_QUOTE","COMMUNITY_NOTE","USER_UPLOAD"],"description":"Every kind this build recognises. The first seven are settlement-grade: they can bear on a verdict once the resolution pipeline retrieves them live from their source. The rest are context, and no quantity of context settles anything. `USER_UPLOAD` is the kind every submitted file receives, whatever it depicts."},"EvidenceKindStatement":{"oneOf":[{"type":"object","required":["known","kind","label","admissibility","canEverSettleACase","statement","rationale","retention"],"properties":{"known":{"type":"boolean","const":true},"kind":{"$ref":"#/components/schemas/EvidenceKind"},"label":{"type":"string","description":"The kind's human label."},"admissibility":{"type":"string","enum":["SETTLEMENT","CONTEXT"]},"canEverSettleACase":{"type":"boolean","description":"Phrased about the kind rather than the item, because a submitter needs to know that improving the provenance of a tweet would not help."},"statement":{"type":"string","description":"What acceptance as this kind means."},"rationale":{"type":"string","description":"Why the kind sits on that side of the line."},"retention":{"type":"string","description":"How long the material is kept, in words."}}},{"type":"object","required":["known","kind","statement"],"properties":{"known":{"type":"boolean","const":false},"kind":{"type":"string","description":"The stored kind string this build cannot read."},"statement":{"type":"string"}}}],"description":"What a submission was accepted as. `known: false` is a real member rather than a fallback: the kind column is free text, and a row written by another deployment can carry a kind this build has never heard of, which is neither context nor settlement."},"ContentHash":{"type":"string","pattern":"^sha256:[0-9a-f]{64}$","description":"`sha256:` followed by the hex digest."},"EvidenceBytes":{"oneOf":[{"type":"object","required":["state","url"],"properties":{"state":{"type":"string","const":"AVAILABLE"},"url":{"type":"string","format":"uri"}}},{"type":"object","required":["state","detail"],"properties":{"state":{"type":"string","const":"NOT_CONFIGURED"},"detail":{"type":"string"}}},{"type":"object","required":["state","detail"],"properties":{"state":{"type":"string","const":"NO_BYTES_SUBMITTED"},"detail":{"type":"string"}}}],"description":"Where a file's bytes are, as three states that cannot be confused. `NOT_CONFIGURED` says a file exists and this deployment kept only its hash — true of every current deployment, which has no evidence object store. `NO_BYTES_SUBMITTED` is a reference item, which never had bytes of its own. An omitted field would read as \"no file\", so neither is omitted."},"EvidenceVerification":{"oneOf":[{"type":"object","required":["method","detail"],"properties":{"method":{"type":"string","const":"FILE_SHA256"},"detail":{"type":"string"}}},{"type":"object","required":["method","domain","canonical"],"properties":{"method":{"type":"string","const":"SNAPSHOT_ENVELOPE"},"domain":{"type":"string","description":"The hash domain: `precrime.evidence.snapshot.v1`."},"canonical":{"type":"string","description":"The exact byte string the hash was taken over, verbatim, so a verifier never has to guess how the body was serialised."}}},{"type":"object","required":["method","detail"],"properties":{"method":{"type":"string","const":"NOT_RECORDED"},"detail":{"type":"string"}}}],"description":"How a third party recomputes the item's `contentHash`. A file is the plain SHA-256 of the retained bytes; a reference is SHA-256 over the domain, a newline and the canonical string. `NOT_RECORDED` is a real answer for a row whose preimage was never published, not a fallback."},"EvidenceAudience":{"type":"string","enum":["PUBLIC","PARTIES_AND_REVIEWERS"],"description":"Who may read the item in principle, as distinct from whether this reader may. A cache holding only the reader's answer would serve a party's authorised read to everybody."},"EvidenceSealReason":{"type":"string","enum":["CASE_NOT_PUBLISHED","UNRECOGNISED_CASE_STATUS","LIVE_DISPUTE","UNRECOGNISED_EVIDENCE_KIND","RECORD_MISMATCH"],"description":"Why an item is off the public record. An empty list is what publication means."},"EvidenceAccessReason":{"type":"string","enum":["PUBLIC_CASE_EVIDENCE","OWN_SUBMISSION","CASE_CREATOR","DISPUTE_PARTY","OPERATOR_REVIEW","CASE_NOT_PUBLISHED","UNRECOGNISED_CASE_STATUS","SEALED_BY_LIVE_DISPUTE","UNRECOGNISED_EVIDENCE_KIND","RECORD_MISMATCH"],"description":"The access decision's reason. A withheld row carries one of the last five; the first five are the grounds on which a read was allowed, listed because the union is one type."},"ReadableEvidence":{"type":"object","required":["evidenceId","acceptedAs","uri","occurredAt","contentHash","submittedAt","audience","bytes","verification"],"description":"One item this reader may see.","properties":{"evidenceId":{"type":"string"},"acceptedAs":{"$ref":"#/components/schemas/EvidenceKindStatement"},"uri":{"type":["string","null"],"description":"Where the material lives, for a reference. Null for a file."},"occurredAt":{"allOf":[{"$ref":"#/components/schemas/NullableTimestamp"}],"description":"When the underlying event happened, when something established it. Null for every submission made through this API: a submitter's claim is kept inside the hashed body under a key naming whose claim it is, never written here as a record of fact."},"contentHash":{"$ref":"#/components/schemas/ContentHash"},"submittedAt":{"$ref":"#/components/schemas/Timestamp"},"audience":{"$ref":"#/components/schemas/EvidenceAudience"},"bytes":{"$ref":"#/components/schemas/EvidenceBytes"},"verification":{"$ref":"#/components/schemas/EvidenceVerification"}}},"WithheldEvidence":{"type":"object","required":["evidenceId","reason","sealedBy"],"description":"An item this reader may not see, listed rather than dropped: a row that vanishes is indistinguishable from one never submitted. Its kind, URI and hash are omitted, and so are the dispute ids, because naming which dispute seals which item is itself disclosure about a live argument.","properties":{"evidenceId":{"type":"string"},"reason":{"$ref":"#/components/schemas/EvidenceAccessReason"},"sealedBy":{"type":"array","items":{"$ref":"#/components/schemas/EvidenceSealReason"}}}},"EvidenceListing":{"type":"object","required":["caseId","observedAt","count","data","withheld","admissibility","verification","note"],"description":"The evidence record of one case, read whole. Capped at 500 rows rather than paginated, because the withheld count and the admissibility split are only honest over the entire record; `count.truncated` says when the cap was hit.","properties":{"caseId":{"type":"string","description":"The public case id."},"observedAt":{"allOf":[{"$ref":"#/components/schemas/Timestamp"}],"description":"The one instant every access decision below was made against."},"count":{"type":"object","required":["onRecord","listed","truncated"],"description":"`listed` counts withheld rows too — the reader was told they exist. Only rows the read never reached are missing, and `truncated` says so.","properties":{"onRecord":{"type":"integer"},"listed":{"type":"integer"},"truncated":{"type":"boolean"}}},"data":{"type":"array","items":{"$ref":"#/components/schemas/ReadableEvidence"}},"withheld":{"type":"array","items":{"$ref":"#/components/schemas/WithheldEvidence"}},"admissibility":{"type":"object","required":["settlement","context","unclassified"],"description":"One entry per readable item, holding its kind name, split the way the dossier renders them. Unclassified is its own bucket rather than dropped.","properties":{"settlement":{"type":"array","items":{"type":"string"}},"context":{"type":"array","items":{"type":"string"}},"unclassified":{"type":"array","items":{"type":"string"}}}},"verification":{"type":"object","required":["algorithm","files","references"],"description":"The two hash recipes, in words, so the listing is verifiable on its own.","properties":{"algorithm":{"type":"string","const":"sha256"},"files":{"type":"string"},"references":{"type":"string"}}},"note":{"type":"string","description":"States that evidence does not decide a verdict."}}},"EvidenceAdmission":{"type":"object","required":["admitted","failures","detail"],"description":"The settlement gate's answer for the item as submitted. Always present, and never admitted for a submission: nothing here read the source, so a pasted chain log is a pointer the pipeline has to re-read live, and the refusal names what it will need to establish.","properties":{"admitted":{"type":"boolean"},"failures":{"type":"array","description":"Every failing requirement, not just the first. Empty when admitted.","items":{"type":"string","enum":["KIND_IS_CONTEXT_ONLY","KIND_NOT_RECOGNISED","PROVENANCE_NOT_LIVE","MISSING_OBSERVED_AT","MISSING_BLOCK_NUMBER","MISSING_TX_HASH","MISSING_SOURCE_URL","MISSING_CONTENT_HASH"]}},"detail":{"type":"string"}}},"AcceptedFile":{"type":"object","required":["contentType","filename","byteLength","metadataHandling","locationMetadataFound","declaredTypeAgreement"],"description":"What was established about a file from its bytes.","properties":{"contentType":{"type":"string","enum":["image/png","image/jpeg","application/pdf"],"description":"Sniffed from the bytes. The only type that may ever be served back."},"filename":{"type":"string"},"byteLength":{"type":"integer","description":"Of the retained bytes."},"metadataHandling":{"type":"string","enum":["STRIPPED","NONE_PRESENT","SCANNED_NOT_REWRITABLE"],"description":"Three states rather than a flag, because \"nothing was removed\" means two different things depending on whether the container could be rewritten at all."},"locationMetadataFound":{"type":"boolean"},"declaredTypeAgreement":{"type":"string","enum":["MATCHED","MISMATCHED","NOT_DECLARED"],"description":"What the sender claimed against what the bytes were. Recorded and never acted on: honest clients routinely misdeclare a type."}}},"SubmissionStanding":{"type":"object","required":["onPublicRecord","audience","sealedBy"],"description":"Where the new row stands. `onPublicRecord` is false for the submitter's own sealed item even though they may read it; the two questions are different.","properties":{"onPublicRecord":{"type":"boolean"},"audience":{"$ref":"#/components/schemas/EvidenceAudience"},"sealedBy":{"type":"array","items":{"$ref":"#/components/schemas/EvidenceSealReason"}}}},"SubmissionReceipt":{"type":"object","required":["evidenceId","caseId","alreadySubmitted","contentHash","acceptedAs","settlement","file","bytes","access","verification"],"description":"What a submitter is handed back, for a file and a reference alike. One shape for both so a client cannot read the absence of a field as a fact: `file` is null for a reference, explicitly. The receipt says what the submission was accepted as, because a bare 201 would tell somebody their screenshot can win the case, and it cannot.","properties":{"evidenceId":{"type":"string"},"caseId":{"type":"string","description":"The public case id."},"alreadySubmitted":{"type":"boolean","description":"True when the same content was already on this case's record. The submission then added nothing to the dossier, and the status is 409 rather than 201."},"contentHash":{"$ref":"#/components/schemas/ContentHash"},"acceptedAs":{"$ref":"#/components/schemas/EvidenceKindStatement"},"settlement":{"$ref":"#/components/schemas/EvidenceAdmission"},"file":{"oneOf":[{"$ref":"#/components/schemas/AcceptedFile"},{"type":"null"}]},"bytes":{"$ref":"#/components/schemas/EvidenceBytes"},"access":{"$ref":"#/components/schemas/SubmissionStanding"},"verification":{"$ref":"#/components/schemas/EvidenceVerification"}}},"OldestPending":{"type":"object","required":["present"],"description":"The age of the oldest waiting job, as a presence union. An empty queue does not have an age of zero: a zero would read on a dashboard as the healthiest possible number and would be indistinguishable from a job enqueued this instant, so the empty case has its own shape and a caller has to handle it.","properties":{"present":{"type":"boolean","description":"False when nothing is waiting at all."},"enqueuedAt":{"$ref":"#/components/schemas/Timestamp"},"ageMs":{"type":"integer","description":"Age in milliseconds, present only when present."}}},"QueueMetrics":{"type":"object","required":["queue","depth","dueNow","inFlight","abandoned","leaseUnknown","failed","deadLettered","oldestPending"],"description":"One queue's state. Each figure exists to separate a pair of situations that look identical from outside — a stalled worker from an idle one, a stuck reclaimer from a healthy one.","properties":{"queue":{"type":"string","description":"The queue name."},"depth":{"type":"integer","description":"Everything still owed: waiting for a first run plus waiting out a backoff."},"dueNow":{"type":"integer","description":"The part of `depth` claimable right now; the rest sits in backoff."},"inFlight":{"type":"integer","description":"Live leases — jobs genuinely being worked."},"abandoned":{"type":"integer","description":"Lapsed leases awaiting reclamation. A figure that does not fall is a stuck reclaimer."},"leaseUnknown":{"type":"integer","description":"Running with no lease: unclassifiable, and needs a human."},"failed":{"type":"integer","description":"Last attempt failed, waiting to retry."},"deadLettered":{"type":"integer","description":"Given up on. Only a replay moves this down."},"oldestPending":{"$ref":"#/components/schemas/OldestPending"}}},"AcceptedToken":{"type":"object","required":["address","symbol","decimals"],"description":"A dollar stablecoin the payment router accepts, identified by address and checked on chain: accepted by the router, six decimals, a printable symbol. Taken at par — one token is one dollar — as a pricing policy; the timelock delists a token that stops being worth one.","properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"symbol":{"type":"string","description":"The token's own `symbol()`. A label only."},"decimals":{"type":"integer"}}},"RevenueSplit":{"type":"object","required":["jarBps","stakerBps","referralBps","companyBps"],"description":"One product family's split, read from the router (decision D-029). The referral share is paid only when an order names a referrer; otherwise it goes to the company, which is paid the remainder, so the four always sum to the amount paid.","properties":{"jarBps":{"$ref":"#/components/schemas/Bps"},"stakerBps":{"$ref":"#/components/schemas/Bps"},"referralBps":{"$ref":"#/components/schemas/Bps"},"companyBps":{"$ref":"#/components/schemas/Bps"}}},"PaymentOrderStatus":{"type":"string","enum":["QUOTED","SUBMITTED","PAID","SETTLED","REFUNDED","EXPIRED","MISMATCHED","CANCEL_REQUESTED"],"description":"QUOTED is written before the quote is signed. PAID, SETTLED and REFUNDED are set only from the router's own events. MISMATCHED is money the router took that does not match what the server sold — a quote it never signed — and activates nothing. SUBMITTED is the browser's report of a transaction, and is advisory."},"PaymentOrder":{"type":"object","required":["id","orderHash","family","productKind","chainId","tokenAddress","amountBaseUnits","payerAddress","settleAfter","status","createdAt"],"description":"One priced, signed purchase. Every amount is the token's base units as a string; `usdCents` is the catalogue price the amount was derived from at par.","properties":{"id":{"type":"string"},"orderHash":{"allOf":[{"$ref":"#/components/schemas/Hash32"}],"description":"keccak256 of the id: what the router keys the order by."},"family":{"type":"string","enum":["SPONSOR","API"]},"productKind":{"type":"string","enum":["SPONSOR_SLOT","API_PLAN","UNKNOWN"]},"productRef":{"type":["object","null"],"additionalProperties":true,"description":"What was sold: the slot and window, or the plan and its length."},"chainId":{"type":"integer"},"routerAddress":{"$ref":"#/components/schemas/EvmAddress"},"tokenAddress":{"$ref":"#/components/schemas/EvmAddress"},"tokenSymbol":{"type":["string","null"]},"tokenDecimals":{"type":["integer","null"]},"usdCents":{"type":["string","null"],"pattern":"^\\d+$"},"amountBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"amount":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}]},"payerAddress":{"$ref":"#/components/schemas/EvmAddress"},"referrerAddress":{"type":["string","null"]},"settleAfter":{"allOf":[{"$ref":"#/components/schemas/Timestamp"}],"description":"When the router may split the money. The Unix epoch for an API plan."},"quoteExpiresAt":{"$ref":"#/components/schemas/NullableTimestamp"},"status":{"$ref":"#/components/schemas/PaymentOrderStatus"},"mismatchReason":{"type":["string","null"],"description":"Why the order needs an operator, when it does."},"submittedTxHash":{"type":["string","null"]},"paidTxHash":{"type":["string","null"]},"paidAmountBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"null"}]},"paidAt":{"$ref":"#/components/schemas/NullableTimestamp"},"settledTxHash":{"type":["string","null"]},"settledAt":{"$ref":"#/components/schemas/NullableTimestamp"},"legs":{"oneOf":[{"type":"object","required":["jarBaseUnits","stakerBaseUnits","referralBaseUnits","companyBaseUnits"],"description":"The legs the router paid at settlement. They sum to the amount paid.","properties":{"jarBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"null"}]},"stakerBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"null"}]},"referralBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"null"}]},"companyBaseUnits":{"oneOf":[{"$ref":"#/components/schemas/BaseUnits"},{"type":"null"}]}}},{"type":"null"}]},"refundedTxHash":{"type":["string","null"]},"refundedAt":{"$ref":"#/components/schemas/NullableTimestamp"},"cancelReason":{"type":["string","null"]},"createdAt":{"$ref":"#/components/schemas/Timestamp"}}},"PaymentQuote":{"type":"object","required":["orderId","family","payer","token","amount","referrer","settleAfter","expiresAt"],"description":"The router's `Quote` struct exactly as signed, integers as decimal strings. Pass it to `PaymentRouter.pay` with the signature; the router refuses it after `expiresAt`, from any wallet but `payer`, and if any field differs from what was signed.","properties":{"orderId":{"$ref":"#/components/schemas/Hash32"},"family":{"type":"integer","description":"1 sponsor slot, 2 API plan."},"payer":{"$ref":"#/components/schemas/EvmAddress"},"token":{"$ref":"#/components/schemas/EvmAddress"},"amount":{"type":"string","pattern":"^\\d+$","description":"Token base units."},"referrer":{"$ref":"#/components/schemas/EvmAddress"},"settleAfter":{"type":"string","pattern":"^\\d+$","description":"Unix seconds."},"expiresAt":{"type":"string","pattern":"^\\d+$","description":"Unix seconds."}}},"Sponsor":{"type":"object","required":["id","name","status","createdAt"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"contactEmail":{"type":["string","null"]},"status":{"type":"string","enum":["PENDING","APPROVED","REJECTED","SUSPENDED"],"description":"Nothing is quoted to a sponsor who is not APPROVED."},"creative":{"type":["object","null"],"additionalProperties":true,"description":"The creative under review: `headline`, `url`, `featuredCaseId`."},"reviewedAt":{"$ref":"#/components/schemas/NullableTimestamp"},"reviewReason":{"type":["string","null"]},"createdAt":{"$ref":"#/components/schemas/Timestamp"}}},"Partner":{"type":"object","required":["id","name","kind","codes","status","createdAt"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["LAUNCHPAD","KOL","GROUP"]},"codes":{"type":"array","items":{"type":"string"},"description":"Codes the owner holds and named as this partner's."},"multiplierBps":{"type":["integer","null"],"description":"As set; null is the default."},"effectiveMultiplierBps":{"type":["integer","null"],"description":"What a launchpad's traders get once it is ACTIVE; null for other kinds."},"status":{"type":"string","enum":["PENDING","ACTIVE","ENDED"]},"contact":{"type":["string","null"]},"pitch":{"type":["string","null"]},"reviewReason":{"type":["string","null"]},"reviewedAt":{"$ref":"#/components/schemas/NullableTimestamp"},"createdAt":{"$ref":"#/components/schemas/Timestamp"},"updatedAt":{"$ref":"#/components/schemas/Timestamp"}}},"CaseReferrals":{"type":"object","required":["wallets","totals","caseCount","cases"],"description":"What each case pool holds for the account's verified wallets, from the pools' `ReferralAccrued` and `ReferralClaimed` logs as the case indexer recorded them: accrued less claimed, claimable once the case has settled, nothing on a void. A pool credits a wallet, and only that wallet can claim, with `claimReferral()` on the pool. Read from the database; a pool the indexer has not reached yet is not in it.","properties":{"wallets":{"type":"array","items":{"$ref":"#/components/schemas/EvmAddress"},"description":"The wallets looked up. Empty means the account has no verified wallet, which is not the same as nothing earned."},"totals":{"type":"object","required":["claimable","pending","claimed"],"description":"Each per asset, never added across assets. A voided case is in none of them.","properties":{"claimable":{"type":"array","items":{"type":"object","required":["asset","symbol","decimals","amountBaseUnits","amount"],"properties":{"asset":{"type":"string","description":"The asset code."},"symbol":{"type":["string","null"]},"decimals":{"type":["integer","null"]},"amountBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"amount":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}]}}}},"pending":{"type":"array","items":{"type":"object","required":["asset","symbol","decimals","amountBaseUnits","amount"],"properties":{"asset":{"type":"string","description":"The asset code."},"symbol":{"type":["string","null"]},"decimals":{"type":["integer","null"]},"amountBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"amount":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}]}}}},"claimed":{"type":"array","items":{"type":"object","required":["asset","symbol","decimals","amountBaseUnits","amount"],"properties":{"asset":{"type":"string","description":"The asset code."},"symbol":{"type":["string","null"]},"decimals":{"type":["integer","null"]},"amountBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"amount":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}]}}}}}},"caseCount":{"type":"integer","description":"Every (case, wallet) row, of which `cases` lists up to 100."},"cases":{"type":"array","description":"Claimable first, then pending, claimed and voided; newest first within each.","items":{"type":"object","required":["caseId","caseTitle","caseStatus","wallet","state","asset","assetSymbol","decimals","stakes","accruedBaseUnits","accrued","claimedBaseUnits","claimed","claimableBaseUnits","claimable","lastAccruedAt","settledAt"],"properties":{"caseId":{"type":"string","description":"The case's public id; its page has the claim."},"caseTitle":{"type":"string"},"caseStatus":{"$ref":"#/components/schemas/CaseStatus"},"wallet":{"$ref":"#/components/schemas/EvmAddress"},"state":{"type":"string","enum":["CLAIMABLE","PENDING","CLAIMED","VOIDED"],"description":"PENDING until the pool's settlement is recorded; VOIDED pays nothing whatever accrued."},"asset":{"type":["string","null"]},"assetSymbol":{"type":["string","null"]},"decimals":{"type":["integer","null"]},"stakes":{"type":"integer","description":"Referred stakes that accrued."},"accruedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"accrued":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}]},"claimedBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"claimed":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}]},"claimableBaseUnits":{"$ref":"#/components/schemas/BaseUnits"},"claimable":{"oneOf":[{"$ref":"#/components/schemas/DecimalAmount"},{"type":"null"}]},"lastAccruedAt":{"$ref":"#/components/schemas/NullableTimestamp"},"settledAt":{"$ref":"#/components/schemas/NullableTimestamp"}}}}}},"PointsEntry":{"type":"object","required":["account","rank","sources","total","multiplierBps","forfeited"],"description":"One address in a live table. Each figure is a decimal string with six places, in the unit the table's `basis` and `units` state.","properties":{"account":{"$ref":"#/components/schemas/EvmAddress"},"rank":{"type":["integer","null"],"description":"The overall place, by total. Null while the weights are sealed."},"sources":{"type":"object","description":"Per source — staking, creator, referral, holding, quality, backing — the figure and the address's place among those with any.","additionalProperties":{"type":"object","properties":{"value":{"type":"string"},"rank":{"type":["integer","null"]}}}},"total":{"type":["string","null"],"description":"Points after multipliers, once revealed."},"multiplierBps":{"type":"integer","minimum":10000,"description":"Partner and loyalty multipliers together: 10,000 is ×1, 12,500 is ×1.25."},"forfeited":{"type":"boolean","description":"A creator slashed this season forfeits it: the row stays, at zero."}}},"Placement":{"type":"object","required":["effect","label","sponsorName","slot","surface","headline","url","featuredCaseId"],"description":"A live, paid placement as it renders. `label` is always the literal word Sponsored, and there is no field by which a placement could ask for a position among organic content.","properties":{"effect":{"type":"string","const":"RENDER_LABELLED_PLACEMENT"},"label":{"type":"string","const":"Sponsored"},"sponsorName":{"type":"string"},"slot":{"type":"string"},"surface":{"type":"string"},"headline":{"type":"string","description":"Plain text, at most 80 characters."},"url":{"type":["string","null"],"description":"An https link, rendered with `rel=\"sponsored nofollow noopener\"`."},"featuredCaseId":{"type":["string","null"]}}},"SideLabels":{"type":"object","required":["GUILTY","INNOCENT","guilty","innocent"],"description":"How a case's two sides are shown (decision D-082). The side values everywhere else stay `GUILTY` and `INNOCENT`, the contract's; a price case and an account market show them as YES and NO, with YES the GUILTY side on chain.","properties":{"GUILTY":{"type":"string","enum":["YES","GUILTY"]},"INNOCENT":{"type":"string","enum":["NO","INNOCENT"]},"guilty":{"type":"string","enum":["Yes","Guilty"]},"innocent":{"type":"string","enum":["No","Innocent"]}}},"FeedPrice":{"type":"object","required":["key","label","assetClass","address","decimals","status","answer","price"],"description":"One registry feed as the price ticker read it. `status` is `OK`, `UNAVAILABLE` (the read failed, or the answer is older than the heartbeat, in open-market time for an equity feed) or `NOT_CONFIGURED` (no RPC); every figure is null unless it is `OK`. Never a settlement source: the resolver reads its own at finalised blocks.","properties":{"key":{"type":"string","description":"The registry key: `BTC_USD`, `EQ_TSLA_USD`."},"label":{"type":"string","description":"The feed's own `description()`."},"asset":{"type":"string"},"assetName":{"type":"string"},"assetClass":{"type":"string","enum":["CRYPTO","DOLLAR","EQUITY"]},"address":{"$ref":"#/components/schemas/EvmAddress"},"decimals":{"type":"integer"},"heartbeatSeconds":{"type":"integer"},"deviationBps":{"type":"integer"},"riskCategory":{"type":["string","null"]},"hyperliquidCoin":{"type":["string","null"]},"status":{"type":"string","enum":["OK","UNAVAILABLE","NOT_CONFIGURED"]},"detail":{"type":["string","null"]},"roundId":{"type":["string","null"],"description":"Decimal string: phase << 64 | round."},"phase":{"type":["integer","null"]},"aggregatorRound":{"type":["string","null"]},"answer":{"type":["string","null"],"description":"In the feed's integers, as a string."},"price":{"type":["string","null"],"description":"The answer in dollars, every digit."},"updatedAt":{"type":["string","null"]},"ageSeconds":{"type":["integer","null"]},"token":{"type":["object","null"],"description":"An equity feed's Stock Token: its multiplier, the staged multiplier and when it takes effect, and whether its price is paused.","additionalProperties":true},"market":{"type":["object","null"],"description":"An equity feed's market state under the current calendar version: `open`, when it changes, and the words (\"open until 20:00 New York time on Friday 2 Oct\").","additionalProperties":true},"hyperliquid":{"type":["object","null"],"description":"On `GET /v1/prices`, for a crypto feed: Hyperliquid's main-dex perp for its asset as context, in the card's own states (`OK`, `NOT_CONFIGURED`, `UNAVAILABLE`, `NO_MARKET`), exactly as `GET /v1/context/hyperliquid` answers. It never settles a case. Null for a depeg or a Stock Token feed, for which Hyperliquid's main exchange lists no market.","additionalProperties":true}}},"CasePrice":{"type":"object","required":["kind","feed","measure","direction","target","latest","recheck"],"description":"A price case's panel, and a Chainlink-priced Stock Token case's (decision D-082): the registry's feed, the target, the baseline and rounds once the resolver has measured them (null before), the ticker's latest answer with its status, the distance to the target, and the feed and round ids to re-check the verdict from any RPC. `marketHours` names the case's own calendar version; `corporateAction` is the Stock Token's state; `depeg` puts a level against $1. Absent on every other type.","properties":{"kind":{"type":"string","enum":["PRICE_MOVE","TOKENIZED_STOCK_SURGE"]},"feed":{"type":"object","additionalProperties":true},"measure":{"type":"string","enum":["TOUCH","CLOSE"]},"direction":{"type":"string","enum":["UP","DOWN"]},"target":{"type":"object","additionalProperties":true},"baseline":{"type":["object","null"],"additionalProperties":true},"threshold":{"type":["object","null"],"additionalProperties":true},"projectedThreshold":{"type":["object","null"],"description":"Before the baseline: the threshold at today's price. Never the target.","additionalProperties":true},"latest":{"oneOf":[{"$ref":"#/components/schemas/FeedPrice"},{"type":"null"}]},"distanceBps":{"type":["integer","null"]},"roundsInWindow":{"type":["string","null"]},"maxRoundGapSeconds":{"type":"integer"},"measured":{"type":["object","null"],"additionalProperties":{"type":"string"}},"recheck":{"type":"object","additionalProperties":true},"depeg":{"type":["object","null"],"additionalProperties":true},"marketHours":{"type":["object","null"],"additionalProperties":true},"corporateAction":{"type":["object","null"],"additionalProperties":true}}},"CaseAccount":{"type":"object","required":["account","coin","side","event","positionNow","positionAtCutoff","recorders","insiderAddresses","insiderStakes","openedByOwner","ownerLink","standingText"],"description":"An account market's panel (decision D-082): the position now (`NOT_CONFIGURED` or `UNAVAILABLE` rather than a guess), the position at the cutoff as each recording states it, how long anyone can re-check from Hyperliquid's own API, each recorder's state (`NOT_CONFIGURED` with no heartbeat, `UNAVAILABLE` when silent for over two minutes), the recordings' fingerprints, the addresses preCrime will not prepare a stake from and any stakes that reached the pool from them, and the owner label with the link that earned it.","properties":{"account":{"$ref":"#/components/schemas/EvmAddress"},"coin":{"type":"string"},"side":{"type":"string","enum":["LONG","SHORT"]},"event":{"type":"string","enum":["LIQUIDATED","FULLY_LIQUIDATED","CLOSED"]},"source":{"type":"string"},"window":{"type":["object","null"],"description":"The recording's span in Hyperliquid's milliseconds: `{fromMs, cutoffMs, untilMs}`, from an hour before the stake cutoff to the window's end.","additionalProperties":true},"positionNow":{"type":"object","additionalProperties":true},"positionAtCutoff":{"type":"object","additionalProperties":true},"recheckHorizon":{"type":["object","null"],"additionalProperties":true},"recorders":{"type":"array","items":{"type":"object","additionalProperties":true}},"recordings":{"type":"array","items":{"type":"object","additionalProperties":true}},"agreedFingerprint":{"type":["string","null"]},"agreedEvents":{"type":"array","items":{"type":"object","additionalProperties":true}},"insiderAddresses":{"type":"array","items":{"type":"object","required":["address","relation"],"properties":{"address":{"$ref":"#/components/schemas/EvmAddress"},"relation":{"type":"string","enum":["SELF","MASTER","SUB_ACCOUNT","VAULT_LEADER","AGENT"]}}}},"insiderStakes":{"type":"array","items":{"type":"object","additionalProperties":true}},"openedByOwner":{"type":"boolean"},"ownerLink":{"type":["object","null"],"description":"`{address, relation, seenAt}`: set exactly when `openedByOwner` is true.","additionalProperties":true},"ownerBanner":{"type":["string","null"]},"standingText":{"type":"string"},"links":{"type":"object","additionalProperties":true}}}}},"x-precrime-documentation-gaps":[]}