---
title: "Portfolio"
method: GET
path: "/v1/portfolios/{slug}"
tags: ["portfolios"]
---

# Portfolio

`GET /v1/portfolios/{slug}`

## Request

- Base URL: `https://api.rayana.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.rayana.ai/v1/portfolios/{slug}`
- Auth: none declared

One book's full detail: registry row, today's holdings + overlay legs, a
recent-events window, the NAV series and windowed returns.

``events`` is the public projection of the diff log, not the log: the most
recent ``_PUBLIC_EVENT_LIMIT`` rows, trimmed to ``_PUBLIC_EVENT_KEYS`` with
each ``reason`` rewritten into the fixed public vocabulary. See the block
comment above — the stored reasons name the selection mechanism and quote
its cut-points, which is the same disclosure ``_public_construction`` exists
to prevent.

Gating: the caller's tier must meet the book's ``tier`` (403 otherwise, so a
free caller sees only the free book). The snapshot actually served is then
clamped by whichever is more restrictive of the caller's ``access_level``
(draft/prime/published — see ``model_portfolio_snapshot_status``, standard
tier and up treated as prime — see ``_effective_as_of``) and its
``publish_delay_min`` free-tier lever; ``asof`` is the snapshot actually
served under that clamp. ``delayed_to`` echoes that clamp date only for a
caller the free-tier lever actually applies to (``_is_delayed``) — a paying
subscriber gets ``None`` even though the draft gate did clamp them, because
waiting for a snapshot to be confirmed is the schedule, not a delay.

## Path parameters

- `slug` string, required

## Headers

- `X-API-Key` string, nullable

## Response `200`

Successful Response

- PortfolioBook
  - `slug` string, required
  - `delayed_to` string, nullable
  - `book` PortfolioRegistry, required: The book's registry row as its detail page sees it: the same merchandising identity as ``ShelfCard``, plus the parsed construction and minus the per-caller flags (the detail endpoint's own gate already answered those).
    - `slug` string, required
    - `display_name` string, nullable
    - `objective` string, nullable
    - `risk_tier` string, nullable
    - `mechanism_blurb` string, nullable
    - `asset_class` string, nullable
    - `benchmark` string, nullable
    - `tier` string, nullable
    - `status` string, nullable
    - `inception_date` string, nullable
    - `construction` PublicConstruction: The book's construction rules, trimmed to what is safe to publish — the same set ``_PUBLIC_CONSTRUCTION_KEYS`` names in api/routers/portfolios.py, which stays the enforcing copy (it runs before this model is built).
      - `holdings_min` integer, nullable
      - `holdings_max` integer, nullable
      - `turnover_tier` string, nullable
      - `exit_rule` string, nullable
      - `distribution_policy` string, nullable
  - `asof` string, nullable
  - `holdings` Holding[]
    - `ticker` string, required
    - `name` string, nullable
    - `sector` string, nullable
    - `cluster` string, nullable
    - `rank` integer, nullable
    - `weight_pct` number, nullable
    - `side` string, nullable
    - `price` number, nullable
    - `entry_date` string, nullable
    - `select_metric` number, nullable
    - `sel_metric_kind` string, nullable
    - `instrument_type` string, nullable
    - `assigned` boolean
    - `effective_basis` number, nullable
  - `overlay` OverlayLeg[]
    - `ticker` string, required
    - `name` string, nullable
    - `instrument_type` string, required
    - `instrument` Contract: The option contract an overlay leg is written on: its identity (expiry/strike/right, per ``_contract_id`` in strategy/model_portfolios.py) plus what a reader needs to price it. Deliberately absent: ``delta`` and ``implied_vol`` — the leg already publishes ``assignment_prob``, which IS |delta| — and ``iv_rank`` / ``vrp`` / ``timing_mult``, the overlay's timing provenance, which is selection mechanism (strategy/premium_overlay.py::contract_spec).
      - `underlying` string, nullable
      - `expiry` string, nullable
      - `strike` number, nullable
      - `right` string, nullable
      - `multiplier` number, nullable
      - `premium` number, nullable
      - `dte` number, nullable
      - `contract` string, nullable
    - `position_group` string, nullable
    - `notional_weight_pct` number, nullable
    - `premium_yield_pct` number, nullable
    - `assignment_prob` number, nullable
    - `price` number, nullable
  - `events` BookEvent[]
    - `event_date` string, nullable
    - `action` string, nullable
    - `ticker` string, nullable
    - `name` string, nullable
    - `side` string, nullable
    - `delta_weight_pct` number, nullable
    - `instrument_type` string, nullable
    - `reason` string, nullable
  - `nav` NavPoint[]
    - `nav_date` string, required
    - `nav` number, nullable
    - `daily_return_pct` number, nullable
    - `cum_return_pct` number, nullable
    - `drawdown_pct` number, nullable
    - `benchmark_ret_pct` number, nullable
    - `n_holdings` integer, nullable
    - `turnover_pct` number, nullable
    - `distribution_pct` number, nullable
    - `capital_nav` number, nullable
    - `cum_distribution_pct` number, nullable
  - `windows` NavWindow[]
    - `label` string, required
    - `return_pct` number, nullable
    - `from_date` string, nullable
    - `covered` boolean
    - `capital_return_pct` number, nullable
    - `distribution_pct` number, nullable
  - `metrics` PortfolioMetrics: Book-level aggregates over today's snapshot (data/reads.py's ``_book_metrics``). The income figures are weight-weighted over the book's TOTAL weight, so an unmeasured name drags the average down rather than being assumed to yield like its peers.
    - `n_holdings` integer
    - `n_overlay` integer
    - `sleeve_state` string, nullable
    - `total_weight_pct` number, nullable
    - `weighted_select_metric` number, nullable
    - `sel_metric_kind` string, nullable
    - `overlay_annual_yield` number, nullable
    - `weighted_income_rate` number, nullable
    - `weighted_sustainable_yield` number, nullable
    - `weighted_reinvest_required` number, nullable
    - `income_coverage` number, nullable
    - `total_income_yield` number, nullable
    - `distribution` DistributionSummary: The book's distribution record (docs/distribution_layer.md §5). ``None`` on ``PortfolioMetrics.distribution`` means not a distributing book at all; present with ``since`` and ``distributions_to_date_pct`` None means it IS distributing but no session has crossed the seam yet — a real state, and it renders differently. Nothing here is annualised. Any surface rendering a distribution or yield figure must render ``sustain_gap_pct`` and ``return_of_capital_pct`` beside it (spec §11, D3).
      - `policy` string, required
      - `since` string, nullable
      - `covered_12m` boolean
      - `distributions_to_date_pct` number, nullable
      - `trailing_12m_yield_pct` number, nullable
      - `sustain_gap_pct` number, nullable
      - `return_of_capital_pct` number, nullable

## Other responses

- `422`: Validation Error

---

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