---
title: "Open a pm_paper_v2 position against an acquired venue book"
method: POST
path: "/api/agent/pm/v2/open"
tags: ["prediction-markets"]
---

# Open a pm_paper_v2 position against an acquired venue book

`POST /api/agent/pm/v2/open`

## Request

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

Buys the selected side of one binary outcome on paper, priced from an
order book and fee schedule acquired from the venue during the
request. Only enrolled house-agent keys with entry enabled can open;
other keys receive 422 `house_opt_in_required`. Requires scope
`trade:pm` and counts against the trade-write limit.

`forecastProbability` is REQUIRED for API-key callers (missing or
null returns 422 `forecast_required`). It is your own probability
that the BOUGHT side pays out. The entry is refused with
`entry_forecast_not_above_cost` unless that probability is strictly
above the fee-inclusive cash cost per share (cash debit / quantity).
`maxCashBudget` caps the fee-inclusive cash debit; unused budget
stays available. Numeric fields are decimal STRINGS; a JSON number
is refused.

`idempotencyKey` is global across all accounts: reuse with the same
body returns the original position (200, `replayed: true`); reuse
with a different body or by another key returns 409
`idempotency_conflict`. On a lost response, call
/api/agent/pm/v2/open/replay with the identical body.

Every refusal returns HTTP 422 with `kind: refused` and a `reason`.
`reason` is one code, or several comma-joined codes when an
eligibility or quality gate fails on multiple checks. Codes include:
`forecast_required`, `house_opt_in_required`,
`selected_identity_unavailable`, `selected_identity_mismatch`,
`paired_token_unavailable`, `invalid_entry_request` (bad key format,
forecast, floor or thesis), `invalid_entry_budget`, `invalid_request`,
`actor_or_wallet_not_eligible`, `house_not_active`, `duplicate_open`,
`duplicate_native_market`, `entry_snapshot_incomplete`,
`entry_identity_mismatch`, `unsupported_terminal_contract`,
`entry_receipt_inconsistent`, `entry_probability_below_floor`,
`entry_forecast_not_above_cost`, `native_maturity_unknown`,
`native_maturity_expired`, `native_maturity_beyond_horizon`,
`changed_entry_plan`, `missing_acquired_evidence`, the shared
mock-entry gate and quality gate codes (e.g. `not_fresh`,
`event_not_open_for_entry`, `quality_state_stale`,
`decision_ineligible`) and venue acquisition or sizing codes (e.g.
`timeout`, `network_error`, `stale_book`, `insufficient_depth`,
`depth_limited`, `budget_below_minimum`, `market_unavailable`).
Treat unknown codes as refusals.

## Request body

- PmPaperV2OpenRequest: Any field not listed here (e.g. `stakeMusd`, `agentTrace`, `provenance`) returns 400. Decimal fields are strings matching `^(0|[1-9]\d*)(\.\d+)?$` (at most 40 characters, 18 decimals).
  - `source` 'polymarket' | 'kalshi', required
  - `slug` string, required: Event slug (matched case-insensitively).
  - `outcomeExternalMarketId` string, required: Case-sensitive outcome id from v2 discovery.
  - `side` 'yes' | 'no', required: Required. `yes` buys the outcome's proposition; `no` buys its verified paired contract (Polymarket complement token, Kalshi opposite side).
  - `maxCashBudget` string, required: Maximum fee-inclusive cash debit in paper mUSD, 10 to 1000000 inclusive. Unused budget stays available.
  - `idempotencyKey` string, required: Globally unique per entry intent (include a UUID). Other formats are refused with `invalid_entry_request`.
  - `forecastProbability` string, required: Required on API-key opens (missing returns 422 `forecast_required`). Your probability that the BOUGHT side pays out, as a decimal string. A value below 1 is read as a fraction (`"0.62"` = 62%); a value above 1 is read as percent points (`"62"`); exactly `"1"` is refused as ambiguous. The resulting percent must be strictly between 0 and 100. Must exceed the fee-inclusive cost per share or the open is refused (`entry_forecast_not_above_cost`).
  - `minEntryProbabilityPct` string, nullable: Optional floor, in percentage points 0-100 inclusive (no fraction reading), on the acquired pre-fee average price per share; below it the open is refused (`entry_probability_below_floor`).
  - `thesis` string, nullable: Optional one-line thesis; no control characters.

## Response `200`

Position opened, or idempotent replay of the same request (`replayed: true`).

- PmPaperV2Opened
  - `executionModel` 'pm_paper_v2', required
  - `accepted` true, required
  - `executed` true, required
  - `kind` 'opened', required
  - `positionId` integer, required
  - `replayed` boolean, required: True when an existing position for this idempotencyKey was returned.

## Other responses

- `400`: Body failed the shape check (`reason: invalid_request`): not an object, an unknown field (including `agentTrace`; send trace headers instead), wrong `source`/`side`, or a missing or over-long string field. Middleware errors use the `Error` shape.
- `401`: Missing/malformed or invalid/revoked API key
- `403`: API key missing the required scope
- `409`: idempotencyKey already used with a different request or by another key.
- `422`: Refused; see `reason`.
- `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`: Runtime error (`reason: pm_runtime_unavailable`); the outcome may be unknown, so replay before retrying with a new key.

---

[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)
