---
title: "Create Paper Deployment"
method: POST
path: "/v1/paper"
tags: ["paper"]
---

# Create Paper Deployment

`POST /v1/paper`

## Request

- Base URL: `https://api.usekeel.io` (the document declares no server; this is the origin it was published from)
- URL: `https://api.usekeel.io/v1/paper`
- Auth: none declared

Start a paper run: one transaction mints the account and the deployment.

## Headers

- `Authorization` string, nullable
- `X-Org-Id` string, nullable

## Request body

- PaperCreateRequest: ``POST /v1/paper`` (S01 §3.9). Unknown keys are refused (422).
  - `strategy_id` string, required
  - `schedule` string, nullable: Optional cron spelling; it must equal the selected commit's derived schedule.
  - `config_id` string, nullable: Optional strategy config to bind the run to (an existing config of this strategy in this org). Absent, the create mints one from the selected commit.
  - `simulated_capital` union: Simulated capital in USD: one of 1000, 10000, 100000.
    - number
    - string

## Response `201`

Successful Response

- PaperDeploymentResponse: The create response: the deployment read plus the paper quota line. ``remaining`` (inherited) carries ``{"paper_slots": N}`` only while the org's free paper slots are strictly below 20% of its cap, the twin of the live deploy's ``{"live_slots": N}``.
  - `deployment_id` string, required
  - `strategy_id` string, required
  - `name` string, required
  - `status` string, required
  - `schedule` string, nullable
  - `evaluation_grid` EvaluationGrid: The candle closes a deployment's runs evaluate (UX-8). Closes sit at ``offset_minutes`` past 00:00 UTC and every ``period_minutes`` after it: the deployment's cron inverted under its persisted dispatch generation (``utils.schedule.evaluation_grid``), so a pre-arm cron that fires at 11:59 reads as the 12:00 close it evaluates.
    - `period_minutes` integer, required
    - `offset_minutes` integer, required
  - `source_hash` string, nullable
  - `s3_key` string, nullable
  - `org_id` string, nullable
  - `account_id` string, nullable
  - `deployed_at` string, nullable
  - `stopped_at` string, nullable
  - `created_at` string, nullable
  - `updated_at` string, nullable
  - `deployed_commit_id` string, nullable
  - `deployed_version_string` string, nullable
  - `config_id` string, nullable
  - `total_pnl` number, nullable
  - `position_count` integer, nullable
  - `venue` VenueBlock: Which venue a deployment or account runs on, and its money class. Paper Trading v2, S01 §3.10 / IC-01-9. ``capital_class`` is ``trading.accounts.capital_class`` (``real`` or ``practice``); ``code`` is the venue's ``trading.venues.code`` (``HL``, ``PAPER``). On a deployment whose ``account_id`` names no account row (no foreign key refuses one, ``V6.0__deployments.sql:16``) the block reads ``{code: null, capital_class: "real"}``: such a row predates paper and keeps its pre-paper meaning. Clients key on this block, never on a probe.
    - `code` string, nullable
    - `capital_class` string, required
  - `remaining` object, nullable
  - `universe_warnings` UniverseWarning[], nullable
    - `symbol` string, required
    - `reason` 'DELISTED' | 'STALE', required
    - `detail` string, required
  - `account_warnings` AccountWarning[], nullable
    - `kind` 'EQUITY_BELOW_VENUE_MINIMUM' | 'ORDERS_SKIPPED_MIN_NOTIONAL' | 'EXITS_BELOW_VENUE_MINIMUM' | 'POSITION_STRANDED_BELOW_MINIMUM', required
    - `account_value` number, required
    - `min_notional` number, required
    - `detail` string, required
    - `orders_skipped` integer, nullable
    - `orders_total` integer, nullable
    - `exits_skipped` integer, nullable
    - `window_days` integer, nullable
    - `exit_symbols` string[], nullable
  - `execution_style` 'market' | 'maker', nullable
  - `paused_reason` string, nullable
  - `paused_at` string, nullable
  - `tranching` object, nullable
  - `health` 'running' | 'degraded' | 'stalled' | 'stopped', nullable
  - `health_detail` HealthDetailResponse: IC-04-2's ``health_detail`` (S04 §3.10.2): the first predicate that matched, the count of most recent consecutive missing bars, the newest COMPLETE bar, and the bar label the current state began at.
    - `reason` 'consecutive_missing' | 'latest_eval_failed' | 'latest_execution_failed' | 'dispatch_exhausted' | 'execution_missing' | 'rail_blocked' | 'funding_stalled' | 'equity_stale' | 'failure_rate' | 'silence' | 'halted' | 'schedule_unreadable', nullable, required
    - `consecutive_missing` integer, required
    - `last_complete_at` string, date-time, nullable, required
    - `since` string, date-time, nullable, required
  - `parity` ParityBlock: IC-03-2: the record's latest non-day-0 snapshot, else its first day-0 row.
    - `residual_bps` number, nullable, required
    - `status` 'tracking' | 'drifting' | 'under_review', required
    - `window_start` string, date-time, nullable, required
    - `window_end` string, date-time, nullable, required
    - `as_of` string, date-time, nullable, required
    - `eval_count` integer, required
    - `meter_eligible` boolean, required
    - `day0_backtest_run_id` string, nullable, required
    - `is_day0` boolean, required
    - `paper_return_pct` number, nullable, required
    - `backtest_return_pct` number, nullable, required
  - `graduation` GraduationBlock: IC-03-5: the graduation criteria state, computed on read. ``record`` and ``carry_over`` are null only for a deployment with no deployed commit (the record has no commit to name).
    - `ready` boolean, required
    - `ready_since` string, date-time, nullable, required
    - `nudged_at` string, date-time, nullable, required
    - `evaluated_at` string, date-time, required
    - `record` GraduationRecord, required: IC-03-5 ``record``: the record the criteria run over (R-27).
      - `started_at` string, date-time, required
      - `commit_id` string, required
      - `commit_seq` integer, required
      - `commit_changed_at` string, date-time, nullable, required
    - `thresholds` GraduationThresholds, required: IC-03-5 ``thresholds``: frozen at create from the defaults version (IC-03-6).
      - `min_age_days` integer, required
      - `min_evals` integer, required
      - `parity_band_bps` number, required
      - `backtest_max_dd_pct` number, nullable, required
      - `dd_floor_pending` boolean, required
      - `defaults_version` integer, required
    - `criteria` GraduationCriteria, required
      - `age` AgeCriterion, required
        - `met` boolean, required
        - `days` integer, required
        - `required_days` integer, required
      - `evals` EvalsCriterion, required
        - `met` boolean, required
        - `count` integer, required
        - `required` integer, required
      - `parity` ParityCriterion, required
        - `met` boolean, required
        - `residual_bps` number, nullable, required
        - `band_bps` number, required
        - `as_of` string, date-time, nullable, required
        - `eval_count` integer, required
        - `meter_eligible` boolean, required
        - `stale` boolean, required
      - `drawdown` DrawdownCriterion, required: Both drawdowns are positive magnitudes; the floor is null while pending.
        - `met` boolean, required
        - `paper_max_dd_pct` number, nullable, required
        - `backtest_max_dd_pct` number, nullable, required
    - `carry_over` CarryOver, required: IC-03-5 ``carry_over`` (S03 §3.9.5, R-32): no simulated capital.
      - `strategy_id` string, required
      - `commit_id` string, required
      - `schedule` string, nullable, required
      - `source_paper_deployment_id` string, required
      - `head_commit_id` string, required
      - `commit_ahead` boolean, required
      - `commit_seq` integer, required
      - `head_commit_seq` integer, required
      - `simulated_capital` string, nullable
      - `min_order_notional` string, nullable
  - `money_freshness` MoneyFreshness: Coverage state of the money facts behind a performance read (spec 04 §5.1, M4). Freshness is part of the answer: the number beside this block is only as fresh as the capture coverage that produced its inputs, and this block says exactly how fresh — * ``COVERED`` — coverage reaches the requested interval's end; ``fresh_as_of`` is the weakest fact class's reach. * ``SYNCING`` — the interval is managed and post-epoch but coverage has not reached its end yet; ``synced_through`` is how far it has (null when no coverage row speaks for the interval at all — absence never reads as fresh). * ``LEGACY`` — the interval predates the account's first coverage row (the epoch cutover): pre-capture history, served ungated but labeled. Until the capture lane goes live, every account reads this. * ``UNMANAGED`` — the interval overlaps no managed window; strategy performance is not defined there. Phase 1 labels only — no numeric field elsewhere in the response changes because of this block.
    - `state` 'COVERED' | 'SYNCING' | 'LEGACY' | 'UNMANAGED', required
    - `fresh_as_of` string, nullable
    - `synced_through` string, nullable
  - `serving_warnings` object[], nullable
  - `known_issues` KnownIssueRef[], nullable
    - `id` string, required
    - `summary` string, required
  - `live_window_warnings` LiveWindowWarning[], nullable
    - `code` 'LIVE_WINDOW_SHORTER_THAN_TRADES', required
    - `message` string, required
    - `backtest_run_id` string, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/usekeel/apis/keel-api.md) · [All operations](https://skmtc.dev/usekeel/apis/keel-api/llms.txt) · [OpenAPI document](https://skmtc.dev/usekeel/apis/keel-api/revisions/3191ca10b0ab?raw)
