> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tilt.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Scenario Analysis Guide

> Analyze how news events and macro shocks affect a portfolio using the Backchannel API.

## Overview

The Backchannel API lets you answer two core questions about a portfolio:

1. **"What would happen to my portfolio if X occurred?"** — Describe any scenario in plain English and get a probability-weighted impact analysis.
2. **"How does this news event affect my holdings?"** — Search curated news scenarios and compute portfolio exposure against them.
3. **"What happens if macro conditions change?"** — Run what-if analysis with specific macro factor shocks (USD strength, oil prices, etc.).

All workflows require a portfolio expressed as a map of `tilt_asset_id → weight`. This guide walks through resolving tickers, building portfolio weights, and calling the scenario and risk endpoints.

## Step 1: Resolve Tickers to Tilt Asset IDs

All portfolio-related endpoints accept weights keyed by **Tilt Asset ID** — an internal identifier for each security. Use the Search Tickers endpoint to resolve symbols.

```bash theme={null}
curl -X GET "https://api.tilt.io/api/v1/tickers/search?symbols=AAPL,MSFT,NVDA" \
  -H "X-Api-Key: YOUR_API_KEY"
```

Response:

```json theme={null}
{
  "count": 6,
  "results": [
    {
      "symbol": "AAPL",
      "name": "Apple, Inc.",
      "exchange": "NAS",
      "ticker_type": "common_stock",
      "tilt_asset_id": "MH33D6-R"
    },
    {
      "symbol": "AAPL",
      "name": "Apple, Inc.",
      "exchange": "TSE",
      "ticker_type": "dr",
      "tilt_asset_id": "W0R2RY-R"
    },
    ...
  ]
}
```

<Note>
  A symbol may return multiple results across exchanges (e.g. NAS and TSE listings). Filter by `ticker_type: "common_stock"` and the relevant exchange to get the primary US listing.
</Note>

Extract the `tilt_asset_id` from each result.

[View API Reference →](/api-reference/endpoint/search-tickers)

<Tip>
  You can resolve multiple symbols in a single call by passing a comma-separated list. Cache the mapping — tilt asset IDs are stable identifiers that don't change.
</Tip>

## Step 2: Build Portfolio Weights

Construct a `portfolio_weights` object mapping each `tilt_asset_id` to a decimal weight (summing to 1.0):

```json theme={null}
{
  "MH33D6-R": 0.40,
  "P8R3C2-R": 0.35,
  "K7TPSX-R": 0.25
}
```

<Note>
  Weights should sum to 1.0. If they don't, the API will still work but exposure calculations will be proportional to the weights provided.
</Note>

This `portfolio_weights` object is reused across all the endpoints below.

## Step 3: Scenario Portfolio Analysis

The simplest way to analyze portfolio impact. Describe any scenario in plain English and the API generates a probability tree of outcomes with portfolio-level returns.

### Recommended: Use `flat_scenarios` mode

We recommend using `flat_scenarios: true` for most integrations. This generates a clean, single-level structure where each scenario directly contains its macro shocks — no nested sub-scenarios to traverse. The result is simpler to parse and display.

```bash theme={null}
curl -X POST "https://tilt.io/api/v1/backchannel/risk/scenario_portfolio_analysis" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "scenario_description": "China begins naval blockade of Taiwan, semiconductor supply chain completely severed",
    "portfolio_weights": {
      "MH33D6-R": 0.40,
      "P8R3C2-R": 0.35,
      "K7TPSX-R": 0.25
    },
    "flat_scenarios": true,
    "search_for_context": true,
    "include_security_details": true
  }'
```

### Key parameters

| Parameter                  | Type    | Default | Description                                                                                                                                                            |
| -------------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `scenario_description`     | string  | —       | Plain English description of the scenario to analyze.                                                                                                                  |
| `portfolio_weights`        | object  | —       | Map of `tilt_asset_id` → weight.                                                                                                                                       |
| `as_of_date`               | string  | today   | Date for the analysis (YYYY-MM-DD).                                                                                                                                    |
| `flat_scenarios`           | boolean | `false` | **Recommended: `true`.** Generates flat scenarios where each scenario directly contains its macro shocks. When `false`, generates a two-level tree with sub-scenarios. |
| `search_for_context`       | boolean | `true`  | Search for relevant background articles to improve scenario quality.                                                                                                   |
| `include_security_details` | boolean | `true`  | Include per-security breakdown in each leaf scenario.                                                                                                                  |
| `aggregate_scenarios`      | boolean | `false` | Collapse the scenario tree into summary stats (mean/std per top-level scenario).                                                                                       |
| `force_regenerate`         | boolean | `false` | Bypass the scenario cache and regenerate from scratch.                                                                                                                 |

### Caching behavior

Scenario generation results are **automatically cached**. The first call for a given scenario generates fresh results (10–15 seconds). All subsequent calls with the same `scenario_description`, `as_of_date`, `search_for_context`, and `flat_scenarios` combination return cached results in **under 1 second**.

The cache is keyed on these four parameters. The portfolio weights are **not** part of the cache key — portfolio impact computation happens after scenario generation, so a single cached scenario tree can be reused for any portfolio instantly.

<Tip>
  **Best practice for article-driven scenarios:** When you ingest a new article, immediately call `scenario_portfolio_analysis` with the article headline/summary as the `scenario_description`. This warms the cache so that any subsequent request — for any portfolio — returns instantly. This is ideal for "preload at login" flows.
</Tip>

Use `force_regenerate: true` to bypass the cache if you need fresh results for an already-cached scenario.

### Response structure

The response includes an AI-generated scenario tree, leaf-level portfolio impacts, and a probability-weighted expected return:

```json theme={null}
{
  "success": true,
  "scenario_description": "China begins naval blockade of Taiwan...",
  "as_of_date": "2026-03-10",
  "expected_portfolio_return_pct": -0.0607,
  "cache_hit": true,
  "portfolio_summary": {
    "num_scenarios": 3,
    "num_leaf_scenarios": 3,
    "num_securities": 3
  },
  "scenario_tree": [
    {
      "probability": 40,
      "title": "Full blockade with military escalation",
      "description": "...",
      "macro_shocks": [
        {"indicator": "VIXCLS", "impact": 0.25},
        {"indicator": "rbics_55", "impact": -0.15}
      ]
    }
  ],
  "leaf_scenarios": [
    {
      "path": ["Full blockade with military escalation"],
      "joint_probability_pct": 40,
      "portfolio_return_pct": -0.12,
      "portfolio_expected_return_pct": -0.12,
      "contribution_to_expected_return_pct": -0.048,
      "top_movers": [
        {"tilt_id": "K7TPSX-R", "ticker": "NVDA", "expected_return_pct": -0.22, "contribution_to_portfolio_pct": -0.055},
        {"tilt_id": "P8R3C2-R", "ticker": "MSFT", "expected_return_pct": 0.01, "contribution_to_portfolio_pct": 0.004}
      ],
      "security_details": [
        {
          "tilt_id": "MH33D6-R",
          "ticker": "AAPL",
          "company_name": "Apple, Inc.",
          "weight": 0.40,
          "expected_return_pct": -0.1559,
          "contribution_to_portfolio_pct": -0.0624,
          "factor_contributions": {}
        }
      ]
    }
  ],
  "context_articles": ["Taiwan Export Growth Lost Momentum in February", "..."],
  "context_article_ids": ["a1b2c3d4-...", "e5f6g7h8-..."],
  "warnings": null,
  "generated_at": "2026-03-10T14:30:00"
}
```

<Note>
  When `flat_scenarios: true`, each scenario in `scenario_tree` contains its `macro_shocks` directly (no `sub_scenarios`), and `leaf_scenarios` has one entry per scenario. When `flat_scenarios: false` (default), scenarios have nested `sub_scenarios` and `leaf_scenarios` contains one entry per sub-scenario with joint probabilities.
</Note>

**Key fields:**

| Field                                                  | Description                                                                                             |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
| `expected_portfolio_return_pct`                        | Probability-weighted expected portfolio return across all leaf scenarios. Decimal form (0.01 = 1%).     |
| `portfolio_summary`                                    | Counts of scenarios, leaf scenarios, and securities analyzed.                                           |
| `scenario_tree`                                        | The full probability tree. Flat when `flat_scenarios: true`, nested with `sub_scenarios` when `false`.  |
| `leaf_scenarios`                                       | Flattened leaf nodes with joint probabilities, portfolio returns, top movers, and per-security details. |
| `leaf_scenarios[].portfolio_return_pct`                | Portfolio return for this specific leaf scenario.                                                       |
| `leaf_scenarios[].contribution_to_expected_return_pct` | This leaf's contribution to the overall expected return (joint\_probability \* portfolio\_return).      |
| `leaf_scenarios[].top_movers`                          | Top 3 gainers and top 3 losers by contribution to portfolio return.                                     |
| `context_articles`                                     | Headlines of articles used as background context (when `search_for_context` is true).                   |
| `context_article_ids`                                  | Point IDs of context articles used as background context.                                               |
| `cache_hit`                                            | `true` if the scenario tree was served from cache, `false` if freshly generated.                        |
| `warnings`                                             | Array of warnings (e.g., unknown tilt\_ids skipped). `null` if no warnings.                             |

<Note>
  This endpoint uses AI to generate scenarios on first encounter (\~10–15 seconds). Cached scenarios return in under 1 second. For instant results on pre-computed news scenarios, use the Search Scenarios endpoint below instead.
</Note>

[View API Reference →](/api-reference/backchannel/scenario-portfolio-analysis)

### Flat vs. hierarchical scenarios

|                      | `flat_scenarios: true`                                   | `flat_scenarios: false` (default)                                                |
| -------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------- |
| **Structure**        | 3 top-level scenarios, each with `macro_shocks` directly | 3–5 top-level scenarios, each with 2–3 `sub_scenarios` containing `macro_shocks` |
| **Leaf count**       | 3 leaves (one per scenario)                              | 6–15 leaves (sub-scenarios)                                                      |
| **Best for**         | Simple UIs, mobile cards, quick summaries                | Detailed analysis, drill-down interfaces                                         |
| **Parse complexity** | Flat list — no nesting to traverse                       | Two levels of nesting                                                            |

## Step 4: Search News Scenarios with Portfolio Exposure

Search for curated, pre-computed news scenarios and see how each one affects your portfolio. This is instant (no AI generation) and useful for browsing recent events.

```bash theme={null}
curl -X POST "https://tilt.io/api/v1/backchannel/scenarios/search" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "queries": [{"weight": 1, "query": "tariffs trade war"}],
    "portfolio_weights": {
      "MH33D6-R": 0.40,
      "P8R3C2-R": 0.35,
      "K7TPSX-R": 0.25
    },
    "aggregate_scenarios": true,
    "limit": 5
  }'
```

### Key parameters

| Parameter             | Type    | Default | Description                                                                                             |
| --------------------- | ------- | ------- | ------------------------------------------------------------------------------------------------------- |
| `queries`             | array   | —       | Weighted search queries. Each has `query` (string) and `weight` (0–1).                                  |
| `subject_code`        | string  | `null`  | Filter by subject code (e.g. `"Technology"`, `"Tariffs & Trade"`). Can be used alone or with `queries`. |
| `portfolio_weights`   | object  | `null`  | Map of `tilt_asset_id` → weight. When provided, each result includes a `portfolio_exposure` field.      |
| `aggregate_scenarios` | boolean | `false` | When `true`, collapses the scenario tree into probability-weighted summary stats.                       |
| `order_by_exposure`   | boolean | `false` | When `true`, sorts results by portfolio impact instead of relevance. Requires `portfolio_weights`.      |
| `limit`               | integer | `10`    | Number of results (1–100).                                                                              |
| `min_importance`      | integer | `null`  | Filter to scenarios with importance rating >= this value (1–10).                                        |

<Note>
  At least one of `queries` or `subject_code` must be provided. Sending an empty request body will return a `422 Validation Error`.
</Note>

### Response structure

The response wraps results in `articles`. Each article is a news scenario with timeline, probability tree, and (when `portfolio_weights` is provided) portfolio exposure:

```json theme={null}
{
  "success": true,
  "articles_found": 5,
  "articles": [
    {
      "point_id": "3fb7a574-c0d7-53b0-96cc-b98a2882f1c7",
      "headline": "Nvidia Invests in Mira Murati's Thinking Machines Lab",
      "date": "2026-03-10T00:00:00",
      "tilt_source": "WSJ",
      "importance_rating": 6,
      "timeline": [
        {"date": "2024-09-28", "summary": "OpenAI CTO Mira Murati resigns amid tensions"},
        {"date": "2026-01-26", "summary": "Nvidia invests $2B in CoreWeave for 5GW AI-factories"}
      ],
      "scenarios": [
        {
          "probability": 34,
          "title": "Compute-fueled breakout",
          "description": "Thinking Machines converts Nvidia's investment into a fast ramp of training and inference capacity...",
          "macro_shocks": [
            {"indicator": "rbics_55", "impact": 0.012},
            {"indicator": "VIXCLS", "impact": -0.6},
            {"indicator": "company_K7TPSX-R", "impact": 0.09}
          ],
          "sub_scenarios": [
            {
              "probability": 60,
              "title": "Frontier model debut",
              "description": "...",
              "macro_shocks": [...]
            }
          ]
        }
      ],
      "portfolio_exposure": {
        "expected_return_pct": 0.0049,
        "mad_pct": 0.0441,
        "num_leaf_scenarios": 8,
        "scenario_level_details": [
          {
            "title": "Compute-fueled breakout",
            "probability_pct": 34,
            "portfolio_return_mean_pct": 0.0693,
            "portfolio_return_std_pct": 0.0309,
            "sub_scenario_count": 2
          }
        ]
      }
    }
  ]
}
```

**Key fields in `portfolio_exposure`:**

| Field                    | Description                                                                                         |
| ------------------------ | --------------------------------------------------------------------------------------------------- |
| `expected_return_pct`    | Probability-weighted expected portfolio return across all leaf scenarios. Decimal form (0.01 = 1%). |
| `mad_pct`                | Mean absolute deviation of returns across leaf scenarios — a measure of outcome uncertainty.        |
| `num_leaf_scenarios`     | Number of leaf nodes in the scenario tree used for the calculation.                                 |
| `scenario_level_details` | Per-scenario breakdown with mean return, standard deviation, and sub-scenario count.                |

[View API Reference →](/api-reference/backchannel/search-scenarios)

## Step 5: Get a Single Scenario with Portfolio Exposure

Once you have a `point_id` from search results, fetch its full detail with portfolio exposure:

```bash theme={null}
curl -X POST "https://tilt.io/api/v1/backchannel/scenarios/3fb7a574-c0d7-53b0-96cc-b98a2882f1c7" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "include_content": true,
    "portfolio_weights": {
      "MH33D6-R": 0.40,
      "P8R3C2-R": 0.35,
      "K7TPSX-R": 0.25
    },
    "aggregate_scenarios": false
  }'
```

When `aggregate_scenarios` is `false`, the response includes `leaf_details` and `scenario_level_details` within `portfolio_exposure`, giving you the full probability tree with per-leaf portfolio returns.

[View API Reference →](/api-reference/backchannel/get-scenario)

## Step 6: Macro Factor Analysis

### Get Macro Sensitivities

Understand which macro factors your portfolio is most exposed to:

```bash theme={null}
curl -X POST "https://tilt.io/api/v1/backchannel/risk/sensitivities" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "portfolio_weights": {
      "MH33D6-R": 0.40,
      "P8R3C2-R": 0.35,
      "K7TPSX-R": 0.25
    },
    "limit": 5
  }'
```

The response ranks macro factors by portfolio-level exposure, showing which factors would move your portfolio the most:

```json theme={null}
{
  "success": true,
  "type": "portfolio",
  "valid_securities": 3,
  "total_weight": 1,
  "portfolio_macro_exposures": [
    {
      "macro_factor": "macro_DTWEXBGS",
      "description": "US Dollar Index (Trade Weighted)",
      "portfolio_exposure": -2.1324,
      "top_contributors": [
        {"tilt_id": "MH33D6-R", "weight": 0.4, "sensitivity": -2.0164, "contribution": -0.8065},
        {"tilt_id": "K7TPSX-R", "weight": 0.25, "sensitivity": -3.1648, "contribution": -0.7912},
        {"tilt_id": "P8R3C2-R", "weight": 0.35, "sensitivity": -1.5277, "contribution": -0.5347}
      ]
    }
  ]
}
```

[View API Reference →](/api-reference/backchannel/get-macro-sensitivities)

### Run a Macro Scenario

Use the sensitivities output to stress-test with specific macro shocks. The `shocks` object maps factor identifiers to shock magnitudes in standard deviations.

<Note>
  The sensitivities endpoint returns factor names with a `macro_` prefix (e.g. `macro_DTWEXBGS`). When passing shocks to the scenario endpoint, **drop the prefix** — use the raw identifier (e.g. `DTWEXBGS`).
</Note>

```bash theme={null}
curl -X POST "https://tilt.io/api/v1/backchannel/risk/scenario" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "portfolio_weights": {
      "MH33D6-R": 0.40,
      "P8R3C2-R": 0.35,
      "K7TPSX-R": 0.25
    },
    "shocks": {
      "DTWEXBGS": 0.5
    },
    "include_security_details": true
  }'
```

The response shows the expected portfolio return and per-security breakdown:

```json theme={null}
{
  "success": true,
  "portfolio_expected_return_pct": -1.0662,
  "shocks_applied": {
    "DTWEXBGS": 0.5
  },
  "factor_summary": [
    {
      "factor": "DTWEXBGS",
      "type": "fred",
      "description": "US Dollar Index (Trade Weighted)",
      "shock": 0.5,
      "portfolio_impact_pct": -1.0662
    }
  ],
  "security_details": [
    {
      "tilt_id": "MH33D6-R",
      "ticker": "AAPL",
      "company_name": "Apple, Inc.",
      "weight": 0.40,
      "expected_return_pct": -0.8066,
      "contribution_to_portfolio_pct": -0.3226,
      "factor_contributions": {
        "DTWEXBGS": -0.8066
      }
    }
  ]
}
```

[View API Reference →](/api-reference/backchannel/run-macro-scenario)

## Putting It Together

A typical integration flow:

1. **Resolve tickers** once and cache the `tilt_asset_id` mapping.
2. **Run scenario portfolio analysis** with a plain English description to quickly assess portfolio impact for any hypothetical event. Use `flat_scenarios: true` for the simplest response structure. This is the easiest starting point.
3. **Search news scenarios** with `portfolio_weights` to find real-world events that matter to the portfolio. Use `order_by_exposure: true` to surface the highest-impact stories first.
4. **Fetch individual scenarios** with `portfolio_weights` for detailed drill-down with leaf-level probability breakdowns.
5. **Get macro sensitivities** to understand the portfolio's factor exposures.
6. **Run macro scenarios** to stress-test against specific factor shocks (e.g., "what if the US Dollar Index rises 0.5 standard deviations?").

<Tip>
  **Scenario Portfolio Analysis** (Step 2) generates scenarios on-the-fly using AI. The first call takes \~10–15 seconds, but results are automatically cached — subsequent calls for the same scenario return in under 1 second, for any portfolio. **Search Scenarios** (Step 3) returns pre-computed news scenarios instantly. Use scenario portfolio analysis for hypothetical what-if questions, and search scenarios for browsing recent real-world events.
</Tip>

## Rate Limits

All endpoints are rate-limited at **120 requests per minute** per API key. The `scenario_portfolio_analysis` endpoint has an additional limit of **10 requests per minute** to protect the AI generation backend.

If you exceed the limit, you'll receive a `429 Too Many Requests` response:

```json theme={null}
{
  "error": "Rate limit exceeded",
  "message": "Too many requests. Please slow down and try again."
}
```

## Error Handling

All error responses follow a consistent format:

```json theme={null}
{
  "error": "Error type",
  "message": "Human-readable description"
}
```

| Status Code | Meaning                                                        |
| ----------- | -------------------------------------------------------------- |
| `400`       | Bad request (e.g., empty portfolio weights)                    |
| `401`       | Missing or invalid authentication. Use the `X-Api-Key` header. |
| `403`       | Invalid API key                                                |
| `415`       | Unsupported Content-Type. Use `application/json`.              |
| `422`       | Validation error with field-level details                      |
| `429`       | Rate limit exceeded                                            |
| `500`       | Internal server error                                          |
