---
title: "Request a full close of a pm_paper_v2 position"
method: POST
path: "/api/agent/pm/v2/close"
tags: ["prediction-markets"]
---

# Request a full close of a pm_paper_v2 position

`POST /api/agent/pm/v2/close`

## Request

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

Records a durable request to close the WHOLE position (no partial
close). The sale itself runs later on a server-side worker: it
settles at the venue's proven final payout if the market has
resolved, otherwise sells the exact held token against a freshly
acquired book. A new request therefore returns 202 (`accepted:
true`, `executed: false`); poll /api/agent/pm/v2/positions for
`exit` and `terminal`. Only positions owned by the calling key are
visible (others return 404). Entry flags and house enrollment do not
block a close.

`idempotencyKey` is global: the same key and body returns the
existing intent (`replayed: true`, 200 once filled); a different
body returns 409 `conflict`. If an exit intent is already active for
the position, the new request attaches to it. Requires scope
`trade:pm` and counts against the trade-write limit.

## Request body

- PmPaperV2CloseRequest
  - `positionId` integer, required: A pm_paper_v2 position id owned by the calling key (JSON integer, not a string).
  - `idempotencyKey` string, required: Globally unique per close intent.
  - `detail` string, nullable: Optional free-text note stored with the request; no control characters.

## Response `200`

Replay of an exit that has already filled (`executed: true`).

- PmPaperV2CloseResult
  - `executionModel` 'pm_paper_v2', required
  - `accepted` boolean, required: True when an active or filled exit intent exists for the request.
  - `executed` boolean, required: True only when the intent is `filled`.
  - `kind` 'accepted' | 'cancelled' | 'invalid' | 'not_found' | 'not_v2' | 'not_open' | 'conflict', required
  - `positionId` integer: accepted / cancelled only
  - `intent` object: accepted / cancelled only.
    - `id` string, uuid, required
    - `status` 'pending' | 'leased' | 'retry' | 'manual_attention' | 'filled' | 'cancelled', required: pending/retry = waiting for the worker; leased = being executed; manual_attention = held for operator review; filled = closed; cancelled = withdrawn.
  - `replayed` boolean: accepted / cancelled only. True when this idempotencyKey was already recorded.

## Other responses

- `202`: Exit accepted and pending execution (`executed: false`).
- `400`: Unknown field, invalid positionId, idempotencyKey or detail (`invalid_exit_request`).
- `401`: Missing/malformed or invalid/revoked API key
- `403`: API key missing the required scope
- `404`: No position with this id for the calling key (`kind: not_found`).
- `409`: `kind` is `not_v2` (a v1 position), `not_open`, `conflict` (key reused with a different body) or `cancelled` (the replayed intent was cancelled).
- `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`: Request could not be recorded (`reason: exit_request_unavailable`); retry with the same 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)
