---
title: "Create Model"
method: POST
path: "/v1/models"
tags: ["Models"]
---

# Create Model

`POST /v1/models`

## Request

- Base URL: `https://api.ensemble.markets` (the document declares no server; this is the origin it was published from)
- URL: `https://api.ensemble.markets/v1/models`
- Auth: HTTP bearer

Create an investment model from a prompt or from your own script.

A model backtests allocations to assets, other models, or both. Existing models referenced by ID, name, or symbol become members. The build (script generation plus a sandboxed backtest at the declared defaults) runs in the background and typically takes 10-60 seconds; this answers at once with the model ID and `status` building. The model's `run` becomes null when work ends; `status` and `error` describe its outcome. With `script`, generation is skipped: the script is validated against the contract and smoke-run. Metered per build (base plus compute; tokens only when generating). `auto_update=false` and `notify_email=false` disable recurring work and allocation emails. Omitting these preserves the existing enabled defaults.

## Request body

- ModelCreateRequest: Create a new investment model from a prompt or from your own script.
  - `prompt` string, nullable: Natural language description of the investment model. A model trades assets, other models, or both; reference your existing models by ID, name, or symbol and they become members. Omit when providing `script`.
  - `script` string, nullable: Your own implementation instead of a prompt. Validated against the same contract as generated code, smoke-run in the sandbox, and stored verbatim. Billed as a build without generation tokens.
  - `name` string, nullable: Model name (defaults to a sequential name like MODEL-0001; editable any time via update)
  - `symbol` string, nullable: Model symbol (defaults to a sequential code like MDL-0001; editable any time via update)
  - `is_favorite` boolean: Favorite this model in your private library
  - `auto_update` boolean, nullable: Initial automatic refresh preference. Omission preserves the legacy enabled default. Set false for one-time research; enabling can also enable component models.
  - `notify_email` boolean, nullable: Initial allocation email preference. Omission preserves the legacy enabled default. Set false unless notifications were requested.
  - `labels` string[]: Private organizational labels, such as Alpaca or Altruist; case-insensitive
  - `effort` 'min' | 'mid' | 'max': How hard the generator works. min (default, all plans): a fast single-shot build. mid (Hobby and Business): a stronger model plus a fidelity review of the script against your description. max (Business): additionally inspects results and refines the implementation. Higher effort bills more per token.
  - `start_date` string, date, nullable: Where the default view of the history begins (YYYY-MM-DD). The full history is always computed and `full_history=true` on any history read reveals it; omission lets start_rule choose. The backtest's start date is an execution input, separate from this view window. A date or period named in the prompt is used when this is omitted.
  - `start_rule` 'two_members' | 'all_members' | 'first_member', nullable: Where the default view begins when no start_date is set. two_members (default): the first day two universe members have data, the earliest a rebalance between them means anything. all_members: the first day every member has data. first_member: the first day any member has data, the full history. Inferred from the prompt when omitted; two_members otherwise.

## Response `202`

Successful Response

- Model
  - `id` string, required: Model ID (model_... or legacy m-...)
  - `name` string, nullable: May be null if the build failed before naming
  - `symbol` string, nullable: May be null if the build failed before naming
  - `status` 'building' | 'active' | 'updating' | 'paused' | 'blocked' | 'failed', required: building: no results yet; active: results served and refreshed nightly; updating: a rebuild or refresh is running (results keep serving); blocked: updates await a failed or unavailable dependency; paused: results served, nightly refresh off; failed: the build never produced a usable model
  - `run` ModelActivity: What is running against the model right now. Null when idle.
    - `kind` 'build' | 'rebuild' | 'run', required: build: first build; rebuild: new implementation from a prompt or script; run: the stored script against new market data (a default run that will go live)
    - `status` 'queued' | 'running', required
    - `stage` string, required: Human-readable current stage, e.g. 'Backtesting'
    - `stage_id` 'n1_security_screening' | 'n1b_universe_resolution' | 'n2_code_generation' | 'n3_code_validation' | 'n4_modal_execution' | 'n5_result_qa', nullable: Pipeline stage id; null for a run
    - `attempt` integer
    - `max_attempts` integer
    - `started_at` string, date-time, nullable
    - `updated_at` string, date-time, nullable
    - `stages` ModelActivityStage[]: Per-stage detail for a build or rebuild; empty for a run
      - `id` 'n1_security_screening' | 'n1b_universe_resolution' | 'n2_code_generation' | 'n3_code_validation' | 'n4_modal_execution' | 'n5_result_qa', required
      - `status` 'pending' | 'running' | 'completed' | 'error', required
      - `attempt` integer
      - `duration_seconds` number, nullable
  - `warning` string, nullable: Something went wrong without changing the status: a failed rebuild, a paused auto-update, or overdue data
  - `depth` integer: 0 when the model holds assets only; otherwise one more than its deepest member
  - `structure` ModelStructure: What the model's whole tree resolves to.
    - `models` integer: Distinct models below this one, at every level
    - `assets` integer: Distinct tickers the tree ultimately holds (cash excluded)
  - `as_of` string, date, nullable: Last market session covered by the results
  - `auto_update` boolean: Whether the nightly refresh is on
  - `refresh` ModelRefresh: Present freshness, independent of the last run's historical outcome.
    - `state` 'current' | 'disabled' | 'pending' | 'running' | 'waiting' | 'retrying' | 'blocked', required
    - `requested_session` string, date, nullable
    - `next_retry` string, date-time, nullable
    - `blockers` ModelRefreshBlocker[]
      - `model_id` string, required
      - `name` string, required
      - `reason_code` string, required
      - `reason` string, required
      - `run_id` string, nullable
  - `is_public` boolean, required
  - `is_favorite` boolean, nullable: Owner's favorite state; null for non-owner readers
  - `labels` string[], nullable: Owner's private organizational labels; null for non-owner readers
  - `url` string, nullable: Tear sheet URL; set once the model has built
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required
  - `description` string, nullable
  - `approved_revision_id` string, nullable
  - `editing_available` boolean
  - `prompt` string, required
  - `universe` string[], nullable: Symbols the model's code actually trades, as recorded by the runtime: tickers, member model IDs, or both. Null until a run has succeeded.
  - `components` ModelRef[]: Models this model holds directly
    - `id` string, required
    - `name` string, nullable
    - `symbol` string, nullable
    - `depth` integer: That model's own depth (0 = assets only)
  - `held_by` ModelRef[]: Models that hold this model directly. It cannot be deleted while any remain.
    - `id` string, required
    - `name` string, nullable
    - `symbol` string, nullable
    - `depth` integer: That model's own depth (0 = assets only)
  - `error` ModelError: Why the build failed. Present only when `status` is failed.
    - `type` string, nullable
    - `message` string, required
    - `code_excerpt` string, nullable: The lines of generated code around the failure, if any
    - `validation` ValidationError[], nullable: The rejection(s), present when the build failed validation
      - `type` string, required: Which rule rejected it: syntax, forbidden, price_source, nondeterministic, constants, entry_point, imports, inputs, members, universe, sources, metadata or other
      - `message` string, required: The rejection, written to teach the fix
      - `line` integer, nullable: The line named by the message, if any
  - `script_url` string, nullable: Download URL for the standalone Python implementation (runtime harness inlined, served as text/x-python). Null until code exists.
  - `window` ModelWindow: The span of history a model is judged over, and why it starts there. `start` and `end` bound the default view: every history endpoint applies them when no window is requested. `full_start` is the first computed bar; it precedes `start` only when the rule hides a partial-universe period, which `full_history=true` reveals.
    - `start` string, nullable: Default view start (YYYY-MM-DD)
    - `end` string, nullable: Last computed bar (YYYY-MM-DD)
    - `rule` 'two_members' | 'all_members' | 'first_member': Where the default view begins when no start_date is set. two_members (default): the first day two universe members have data, the earliest a rebalance between them means anything. all_members: the first day every member has data. first_member: the first day any member has data, the full history.
    - `start_date` string, nullable: View start set on the model, or null when the rule decides
    - `full_start` string, nullable: First computed bar; equals `start` when nothing is hidden
    - `constrained_by` string[]: Members whose inception set `start` under the rule; empty for an explicit date
  - `start_date` string, date, nullable: Where the default view of the history begins (YYYY-MM-DD). The full history is always computed and `full_history=true` on any history read reveals it; omission lets start_rule choose. The backtest's start date is an execution input, separate from this view window.
  - `start_rule` 'two_members' | 'all_members' | 'first_member': Where the default view begins when no start_date is set. two_members (default): the first day two universe members have data, the earliest a rebalance between them means anything. all_members: the first day every member has data. first_member: the first day any member has data, the full history.
  - `availability` ModelAvailability: Data availability recorded by the run: raw facts, not settings.
    - `partial` string, nullable: First computed bar (some members may still be missing)
    - `complete` string, nullable: First bar on which every universe member has data
    - `members` object, nullable: First bar with data for each universe member, {symbol: date}
    - `members_last` object, nullable: Last bar with data for each universe member, {symbol: date}, as the run saw it. A member behind the others lagged the final bar or has ended; the latest target was decided without it.
    - `backfills` object, nullable: History extensions the run applied, {symbol: source}. Present when a holding's series was extended before its own inception.
  - `schedule` ModelSchedule: Nightly auto-update state. Null until the model has succeeded once.
    - `enabled` boolean, required
    - `notify_email` boolean, required
    - `next_run_at` string, date-time, nullable
    - `last_run_at` string, date-time, nullable
    - `last_outcome` 'updated' | 'unchanged' | 'skipped' | 'failed', nullable: updated: new session folded in; unchanged: no new session yet; skipped/failed: the run did not complete; automatic-update preferences are unchanged
    - `last_error` string, nullable
    - `last_notified_at` string, date-time, nullable
  - `usage` ModelUsage: Billing usage for the most recent generation.
    - `units` integer, required
    - `base` integer, nullable
    - `tokens` integer, nullable
    - `seconds` integer, nullable
    - `effort` 'min' | 'mid' | 'max', nullable: The effort the build was billed at
  - `metadata` ModelMetadata: Run metadata computed by the runtime. Extra keys are passed through.
    - `universe` string[], nullable
    - `member_sources` object, nullable: Resolved member model/run identities and script hashes used by this result, including nested sources.
    - `start_date` string, nullable
    - `end_date` string, nullable
    - `initial_cash` number, nullable
    - `final_value` number, nullable
    - `trades_total` integer, nullable
    - `rebalance_dates` integer, nullable
    - `rebalances_per_year` number, nullable
    - `annual_turnover` number, nullable
    - `max_gross_exposure` number, nullable
    - `harness_version` string, nullable
    - `backfills` object[], nullable
    - `optimizers` unknown
    - `trade_log_truncated` boolean, nullable
    - `target_history_truncated` boolean, nullable
  - `custom_keys` ModelCustomKeys: Names recorded in a model's custom diagnostics, grouped by shape.
    - `series` string[]: Keys under `series`
    - `values` string[]: Keys under `values`
  - `inputs_schema` object, nullable: JSON Schema for `inputs` on a run, with defaults: the script's declared inputs followed by the platform's `start_date` (format date, default null). A run at the defaults computes the full history and goes live; a run with a `start_date` trades nothing before it and is a snapshot.
  - `live_run_id` string, nullable: The default run whose result is the live history
  - `estimate_cents` integer, nullable: On the 202 from create and rebuild: what the queued build holds against the balance, in cents. The charge that lands when it finishes replaces this figure. Null on every other read.
  - `build_id` string, nullable: The build that produced the current script; on a failed program, the failed build, so getBuild shows what was billed. getBuild returns it.
  - `is_owner` boolean: True when the caller owns this model

## Other responses

- `4XX`: The request was refused; `error.type` says why
- `5XX`: The server failed; quote `X-Request-Id` when reporting it

---

[API](https://skmtc.dev/ensemble/apis/ensemble-api.md) · [All operations](https://skmtc.dev/ensemble/apis/ensemble-api/llms.txt) · [OpenAPI document](https://skmtc.dev/ensemble/apis/ensemble-api/revisions/4c0fbd80c197?raw)
