---
title: "Discover candidates for the acquired-book PM paper model (pm_paper_v2)"
method: GET
path: "/api/agent/pm/v2/discover"
tags: ["prediction-markets"]
---

# Discover candidates for the acquired-book PM paper model (pm_paper_v2)

`GET /api/agent/pm/v2/discover`

## Request

- Base URL: `https://api.coinrithm.com`
- URL: `https://api.coinrithm.com/api/agent/pm/v2/discover`
- Auth: HTTP bearer

Candidate list for `pm_paper_v2`, the paper model that fills against
an order book acquired from the venue at entry time. Only keys
enrolled as a server-configured house agent may call it; any other
key receives 403 `house_opt_in_required`.

Built from the same cached discovery set as /api/agent/pm/discover
(all sources, `best` sort, offset 0), then filtered per request: an
outcome is kept only when its exact stored native identity resolves
(Polymarket condition + token, or Kalshi ticker + side), it is not a
Polymarket negative-risk contract, and, when the house has a
`maxEndDays` bound, its scheduled native end falls inside it. Events
left with no outcome are dropped; `meta.boundedSelection` counts
what was removed and why. Dates are stored provider fields, not a
settlement guarantee; entry re-acquires identity, maturity, book and
fees. Only `q` and `limit` are accepted; any other query parameter
returns 400. The legacy `quoteHint` on each market still points at
the v1 quote endpoint. Requires scope `read`.

## Query parameters

- `q` string
- `limit` integer

## Response `200`

OK

- PmPaperV2DiscoveryResponse
  - `executionModel` 'pm_paper_v2', required
  - `data` PmPaperV2DiscoveryMarket[], required
    - `source` 'kalshi' | 'polymarket'
    - `slug` string
    - `title` string
    - `endDate` string, date-time, nullable
    - `relatedCoins` string[]: CoinRithm slugs of the coins this event is DIRECTLY about (admin- rejected links excluded; the same set the public event page shows), de-duplicated and sorted. Empty = no linked coin. Lets an agent group separate events on one coin and horizon (opt-in cross-event exposure cap); it is not a correlation model. Absent on older API versions.
    - `freshness` Freshness: Data-freshness descriptor. Futures + spot use ageSeconds; PM uses ageMinutes. `status` is a freshness label; `basis` (PM only) names which timestamp the age was measured against.
      - `asOf` string, date-time, nullable
      - `ageSeconds` number, nullable: futures / spot
      - `ageMinutes` number, nullable: PM
      - `status` 'fresh' | 'lagging' | 'stale' | 'unknown' | 'null', nullable: fresh | lagging (PM only) | stale | unknown. PM lagging>=45m & stale>=2h; spot/futures stale>120s.
      - `basis` 'latest_snapshot' | 'source_update' | 'processed' | 'event_update' | 'unknown', nullable: PM only — which timestamp the age was measured against.
    - `pinned` boolean: True when the market is effectively decided (leading outcome at/ above the pinned-probability threshold). Deranked in this listing — agents can skip these without recomputing.
    - `eligible` boolean, nullable: Can an agent open this binary book right now (shared mock-entry gate)? null when scalars are unavailable. Eligible markets are listed first.
    - `eligibleBlockReasons` string[]: Structured reasons the market is not openable (e.g. multi-outcome / non-binary / settled). Empty when eligible or unknown.
    - `quality` PmQuality: Persisted quality assessment from CoinRithm's truth engine — the aggregator's proven, versioned verdict for this event (one current state per event, updated when facts change). Markets with critical failures remain visible everywhere; `decisionEligible: false` means new paper opens are BLOCKED (pm/open returns 422 with these stored reasons) and alerts are suppressed. Omitted entirely when no assessment row exists yet (brand-new events) — never fabricated.
      - `decisionEligible` boolean: Fresh, structurally valid, metrics-supported, open/quoteable, and not source-degraded.
      - `warningReasons` string[]: Non-blocking flags (e.g. anomaly_flagged). Informational.
      - `blockReasons` string[]: Stable decision-blocking codes (stale_freshness, quote_dead, dead_zero, unpriced, not_open, freshness_unknown).
      - `policyVersion` string: Version of the quality policy that produced this verdict (e.g. pm-quality-2).
      - `assessedAt` string, date-time, nullable: Source-capture time backing the verdict (freshness SSOT), not a row-write timestamp.
    - `outcomes` object[]
      - `externalMarketId` string
      - `name` string
      - `probability` number: 0..100
      - `tokenId` string, nullable
      - `eligible` boolean, nullable: Per-outcome openability (structural + 0<p<100 live yes fill). null when scalars are unavailable.
      - `rules` PmDiscoveryOutcomeRule: This outcome's own settlement rule as the provider states it, bound by provider market id (Kalshi ticker = `externalMarketId`), never by title words. `unknown` when the market is not in the provider data, has no rule, or is listed twice with different text (never resolved silently).
        - `status` 'exact' | 'unknown', required
        - `basis` 'provider_market_rules', required
        - `marketId` string, required
        - `primary` string: exact only; cut with … when truncated
        - `secondary` string, nullable: exact only. null when the outcome has no secondary rule or it is the shared one in `resolution.rules` (`secondaryShared` true).
        - `secondaryShared` boolean: exact only
        - `truncated` boolean: exact only; primary or secondary was cut at 700 characters
        - `reason` 'source_rules_unavailable' | 'market_not_found' | 'rule_missing' | 'conflicting_duplicates': unknown only. `source_rules_unavailable`: the venue's per-market rules could not be read at all; `market_not_found`: they were read but do not include this market.
      - `nativeMarket` PmPaperV2NativeMarket, required
        - `venue` 'polymarket' | 'kalshi', required
        - `key` string, required: Venue market key independent of side and token: `polymarket:<conditionId, lowercased>` or `kalshi:<ticker>`. One open v2 position per key per wallet (`duplicate_native_market`).
      - `nativeEndAt` string, date-time, nullable, required: Stored venue scheduled end (UTC); null when absent or invalid.
      - `nativeEndBasis` 'gamma_market_endDate' | 'kalshi_market_close_time', required
      - `settlementTimeKnown` false, required: A scheduled end is not an observed settlement time.
    - `volume24h` number
    - `liquidity` number
    - `spread` number, nullable
    - `decisionSupport` DecisionSupport: Pre-computed market-quality grade for a prediction market (the same builder the web event/hub cards use): a quality score + tiered liquidity/volume/spread + risk flags. Lets an agent gauge tradability without running its own analysis. Returned by get_market_context's relatedMarkets and by pm/quote.
      - `qualityScore` number
      - `qualityTier` 'high' | 'medium' | 'low'
      - `spreadTier` 'tight' | 'moderate' | 'wide' | 'unknown'
      - `liquidityTier` 'high' | 'medium' | 'low' | 'unknown'
      - `volumeTier` 'high' | 'medium' | 'low' | 'unknown'
      - `flags` object
        - `thinMarket` boolean
        - `inactiveMarket` boolean
        - `highAmbiguity` boolean
        - `nearResolution` boolean
    - `resolution` PmDiscoveryResolution
      - `published` boolean, required: False when the venue publishes no real rule (a question-only body); `rules` is then null and the settlement terms are unknown.
      - `rules` string, nullable, required: The de-duplicated rule text joined into one string: the condition, the "otherwise" branch and the resolution source when the venue states them. Cut at 700 characters with "…".
      - `rulesTruncated` boolean, required
      - `settlementSource` string, nullable, required
      - `settlementSources` object[], nullable, required: Named outlets the venue says it settles from (Kalshi); null elsewhere.
        - `name` string, required
        - `url` string, nullable, required: http(s) only; anything else is null
      - `scope` 'per_outcome': Present for venues whose markets each state their own rule (Kalshi), even when those rules could not be read (every outcome is then `unknown`); absent for other venues, where `rules` is the event text as before. `per_outcome`: each outcome carries its own exact `rules`, bound by provider market id, and this event-level `rules` is ONLY a secondary rule every market states identically (compared after whitespace normalization), else null. The event-level bullets, which mix several markets' conditions, are not served. `published` is true when at least one outcome has an exact rule.
    - `quoteHint` PmDiscoveryQuoteHint
      - `endpoint` string
      - `source` 'kalshi' | 'polymarket'
      - `slug` string
      - `stakeMusdMin` number
      - `outcomeExternalMarketIdField` string
  - `pagination` object, required: Page of the unfiltered event list (offset is always 0).
    - `limit` integer
    - `offset` 0
    - `hasMore` boolean
  - `meta` object, required
    - `source` 'all'
    - `sources` string[]
    - `sourceHealth` object[]
      - `slug` string
      - `lastIngestAt` string, date-time, nullable
      - `ingestAgeSeconds` integer, nullable
      - `status` 'fresh' | 'stale' | 'never_ingested'
    - `sort` 'best'
    - `q` string, nullable
    - `maxEndDays` integer, nullable, required: The house's scheduled-end horizon in days; null = no horizon filter.
    - `nativeIdentityBasis` 'stored_exact_selected_market', required
    - `horizonBasis` 'scheduled_native_end_not_settlement_guarantee', required
    - `evaluatedAt` string, date-time, required
    - `boundedSelection` object, required
      - `requestedEventLimit` integer, required
      - `eventsBefore` integer, required
      - `outcomesBefore` integer, required
      - `eventsAfter` integer, required
      - `outcomesAfter` integer, required
      - `excluded` object, required: Outcomes removed, counted by reason: `native_identity_unavailable`, `unsupported_terminal_contract`, `native_maturity_unknown`, `native_maturity_expired`, `native_maturity_beyond_horizon`.
      - `allMarketCoverage` false, required: The list is a bounded sample, not every eligible market.
    - `note` string, required

## Other responses

- `400`: Unsupported or invalid query parameter (`invalid_discovery_query`).
- `401`: Missing/malformed or invalid/revoked API key
- `403`: Missing scope, or the key is not an enrolled house agent (`house_opt_in_required`).
- `429`: Rate limit exceeded for this API key (120 req/min baseline, 20 trade-writes/min). Honor `Retry-After`, then resume; the `RateLimit-*` headers on every response let you pace proactively. (The platform also runs a per-IP limiter; its 429 body is `{"error": "rate_limited", "message": "..."}`.)
- `503`: Native discovery read failed (`pm_native_discovery_unavailable`).

---

[API](https://skmtc.dev/coinrithm/apis/coinrithm-agent-trading-api.md) · [All operations](https://skmtc.dev/coinrithm/apis/coinrithm-agent-trading-api/llms.txt) · [OpenAPI document](https://skmtc.dev/coinrithm/apis/coinrithm-agent-trading-api/revisions/28fb59f285fe?raw)
