---
title: "Open (or add to) a mock futures position"
method: POST
path: "/api/agent/futures/open"
tags: ["futures"]
---

# Open (or add to) a mock futures position

`POST /api/agent/futures/open`

## Request

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

Requires scope `trade:futures`. `idempotencyKey` is REQUIRED and unique
per intent (reuse replays the result). One net position per coin: a
second open on the same coin/side ADDS to it (same leverage; opposite
side rejected). Returns 403 only if futures is later disabled.

## Request body

- FuturesOpenRequest
  - `coinId` string, required
  - `side` 'long' | 'short', required
  - `leverage` number, required
  - `marginMusd` number, required
  - `idempotencyKey` string, required: Unique per intent. Reusing it replays the original result.
  - `stopLossPrice` number, nullable: Optional open-time resting stop-loss. Side-aware: long requires liq < SL < entry mark; short requires entry mark < SL < liq (rejected as a dead trigger otherwise). Fired by the per-minute worker; the FULL position settles at mark with exitReason=stop_loss. Not accepted on an ADD — use /futures/sl-tp.
  - `takeProfitPrice` number, nullable: Optional open-time resting take-profit (long: above mark; short: below). Same worker semantics, exitReason=take_profit.
  - `agentTrace` AgentTraceMetadata: Optional private trace metadata supplied by a user-run agent. CoinRithm stores only this structured summary; do not send chain-of-thought, secrets, emails, or private account identity.
    - `runId` string, nullable
    - `decisionId` string, nullable
    - `strategyLabel` string, nullable
    - `confidence` number, nullable
    - `rationaleSummary` string, nullable

## Response `200`

Added to existing position, or idempotent replay

- FuturesPositionEnvelope
  - `position` FuturesPosition: Mock futures position. Live-mark fields (markPrice, unrealizedPnlMusd, liquidationDistancePct, atLiquidation) are added only on OPEN positions in the list endpoint; they may be null when no live mark is available.
    - `id` integer
    - `status` 'open' | 'closed' | 'liquidated'
    - `coin` object
      - `ucid` string
      - `symbol` string
      - `name` string
    - `side` 'long' | 'short'
    - `leverage` number
    - `entryPrice` number
    - `marginMusd` number
    - `notionalMusd` number
    - `sizeCoin` number
    - `maintenanceMarginRate` number
    - `liquidationPrice` number
    - `freshnessAtEntry` 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.
    - `stopLossPrice` number, nullable: Resting stop-loss trigger (null = none).
    - `takeProfitPrice` number, nullable: Resting take-profit trigger (null = none).
    - `exitPrice` number, nullable
    - `exitReason` string, nullable: user_close | liquidation | stop_loss | take_profit
    - `realizedPnlMusd` number, nullable
    - `fillModel` string, nullable: Fill model pinned when the position opened; null for legacy mark fills.
    - `openedAt` string, date-time, nullable
    - `closedAt` string, date-time, nullable
    - `createdAt` string, date-time
    - `markPrice` number, nullable: list endpoint, open positions only
    - `unrealizedPnlMusd` number, nullable
    - `fundingPaidMusd` number, nullable: Signed cumulative funding applied in mUSD (positive = paid, negative = received); omitted or null when unavailable. Zero is a meaningful value.
    - `fundingAppliedThrough` string, date-time, nullable: Latest timestamp through which cumulative funding was applied; omitted or null when unavailable.
    - `currentFundingReference` FuturesFundingSource: Current venue/contract funding reference. Historical charges are represented by funding events.
      - `venue` string, required: Current funding venue identifier.
      - `symbol` string, required: Current venue perpetual symbol.
      - `fundingIntervalHours` integer, required: Current venue funding interval in hours.
    - `liquidationDistancePct` number, nullable
    - `atLiquidation` boolean, nullable
  - `idempotentReplay` boolean: present (true) on a replay

## Other responses

- `201`: New position opened
- `400`: Invalid or missing parameters. On a route with a query allowlist, an undocumented query parameter returns `{"error": "unsupported_query_parameter", "message": "Unsupported query parameter: <key>. Supported: <keys>."}`.
- `401`: Missing/malformed or invalid/revoked API key
- `403`: Missing scope OR futures opening disabled (server flag)
- `404`: No active mock_spot wallet for this user
- `409`: idempotencyKey already used (by a different intent/user); `hosted_agent_not_active` when the owner paused this hosted agent; `funding_settlement_pending` (retry after `retryAfterSeconds`) or `position_liquidated_by_funding`
- `422`: Entry blocked by the eligibility/risk gate
- `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`: Could not open due to contention; retry

---

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