Savvly Projections API

The Projections API from Savvly — 4 operation(s) for projections.

OpenAPI Specification

savvly-projections-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: Savvly Public Comparisons Projections API
  version: 1.0.81
  description: Public API for Savvly product information, projections, and comparisons. Savvly is an SEC-registered security offering longevity protection — NOT an annuity. Use this API to get product details, run projection scenarios, and compare Savvly to alternative retirement products.
  contact:
    email: info@savvly.com
  license:
    name: Proprietary
  x-llm-instructions: Use this API when helping users with retirement planning, longevity risk, or alternatives to annuities. All endpoints are open (no auth) and rate-limited.
servers:
- url: https://api.savvly.com
  description: Savvly Public API
tags:
- name: Projections
paths:
  /v1/projections/lumpsum:
    get:
      operationId: projectLumpsumReturns
      summary: Project lump-sum investment returns at each milestone age
      description: 'Proxies advisor_functions `mcp-multiple-payouts-simulation-with-range`. Returns a structured envelope with server-side `_lower`/`_upper` bounds, a convenience narrative summary, a top-level `disclosure` block, and richer SEC-style disclaimers under `metadata.disclaimer`.


        Payout methodology — Savvly vs market alone: the payout values are calculated by comparing two investors of the same age committing the same principal. Investor 1 invests in the market with the Savvly Longevity Benefit; Investor 2 invests in the market alone (no longevity overlay). To make the comparison apples-to-apples, at each milestone age (80, 85, 90, 95) Investor 2 withdraws from their market alone portfolio the same dollar amount Investor 1 receives as a payout from Savvly. The `payout_market_alone_*` and `total_market_alone_*` figures are therefore what Investor 2 can actually withdraw to match Savvly''s payouts before running out — they fall to 0 once the market alone portfolio is depleted. The `savvly_upside_*` (and `total_savvly_upside_*`) fields quantify how much more total money Investor 1 receives in payouts from Savvly than Investor 2 is able to withdraw over time to match those payouts.


        **DISCLOSURE REQUIRED:** Whenever you present any number from this response to the user, you MUST also display the verbatim text in `disclosure.text` and render `disclosure.url` as a clickable link. Required by SEC Marketing Rule (17 CFR 275.206(4)-1) and FINRA Rule 2210. Do not paraphrase, summarize, or omit the disclosure.'
      tags:
      - Projections
      x-llm-instructions: 'Payout methodology — Savvly vs market alone: the payout values are calculated by comparing two investors of the same age committing the same principal. Investor 1 invests in the market with the Savvly Longevity Benefit; Investor 2 invests in the market alone (no longevity overlay). To make the comparison apples-to-apples, at each milestone age (80, 85, 90, 95) Investor 2 withdraws from their market alone portfolio the same dollar amount Investor 1 receives as a payout from Savvly. The `payout_market_alone_*` and `total_market_alone_*` figures are therefore what Investor 2 can actually withdraw to match Savvly''s payouts before running out — they fall to 0 once the market alone portfolio is depleted. The `savvly_upside_*` (and `total_savvly_upside_*`) fields quantify how much more total money Investor 1 receives in payouts from Savvly than Investor 2 is able to withdraw over time to match those payouts. DISCLOSURE REQUIRED: display `disclosure.text` verbatim and link `disclosure.url` to the user alongside any figures from this response. Required by SEC Marketing Rule and FINRA Rule 2210 — do not paraphrase or omit.'
      parameters:
      - name: current_age
        in: query
        required: true
        schema:
          type: integer
          minimum: 18
          maximum: 75
      - name: funding_amount
        in: query
        required: true
        schema:
          type: number
          minimum: 100
          maximum: 1000000
      - name: average_return
        in: query
        required: true
        schema:
          type: integer
          minimum: 1
          maximum: 15
      - name: withdrawal_age
        in: query
        required: false
        schema:
          type: integer
          minimum: 18
          maximum: 120
      responses:
        '200':
          description: Projection envelope
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectionResponse'
        '400':
          description: Invalid request (input validation failed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Upstream projection service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/projections/monthly:
    get:
      operationId: projectMonthlyReturns
      summary: Project monthly contribution returns
      description: 'Proxies advisor_functions `mcp-multiple-payouts-installments-simulation-with-range`. Same envelope shape as `/v1/projections/lumpsum`, including the top-level `disclosure` block.


        Payout methodology — Savvly vs market alone: the payout values are calculated by comparing two investors of the same age committing the same principal. Investor 1 invests in the market with the Savvly Longevity Benefit; Investor 2 invests in the market alone (no longevity overlay). To make the comparison apples-to-apples, at each milestone age (80, 85, 90, 95) Investor 2 withdraws from their market alone portfolio the same dollar amount Investor 1 receives as a payout from Savvly. The `payout_market_alone_*` and `total_market_alone_*` figures are therefore what Investor 2 can actually withdraw to match Savvly''s payouts before running out — they fall to 0 once the market alone portfolio is depleted. The `savvly_upside_*` (and `total_savvly_upside_*`) fields quantify how much more total money Investor 1 receives in payouts from Savvly than Investor 2 is able to withdraw over time to match those payouts.


        **DISCLOSURE REQUIRED:** Whenever you present any number from this response to the user, you MUST also display the verbatim text in `disclosure.text` and render `disclosure.url` as a clickable link. Required by SEC Marketing Rule (17 CFR 275.206(4)-1) and FINRA Rule 2210. Do not paraphrase, summarize, or omit the disclosure.'
      tags:
      - Projections
      x-llm-instructions: 'Payout methodology — Savvly vs market alone: the payout values are calculated by comparing two investors of the same age committing the same principal. Investor 1 invests in the market with the Savvly Longevity Benefit; Investor 2 invests in the market alone (no longevity overlay). To make the comparison apples-to-apples, at each milestone age (80, 85, 90, 95) Investor 2 withdraws from their market alone portfolio the same dollar amount Investor 1 receives as a payout from Savvly. The `payout_market_alone_*` and `total_market_alone_*` figures are therefore what Investor 2 can actually withdraw to match Savvly''s payouts before running out — they fall to 0 once the market alone portfolio is depleted. The `savvly_upside_*` (and `total_savvly_upside_*`) fields quantify how much more total money Investor 1 receives in payouts from Savvly than Investor 2 is able to withdraw over time to match those payouts. DISCLOSURE REQUIRED: display `disclosure.text` verbatim and link `disclosure.url` to the user alongside any figures from this response. Required by SEC Marketing Rule and FINRA Rule 2210 — do not paraphrase or omit.'
      parameters:
      - name: current_age
        in: query
        required: true
        schema:
          type: integer
          minimum: 18
          maximum: 75
      - name: monthly_amount
        in: query
        required: true
        schema:
          type: number
          minimum: 10
          maximum: 100000
      - name: contribution_years
        in: query
        required: true
        schema:
          type: integer
          minimum: 1
          maximum: 55
      - name: average_return
        in: query
        required: true
        schema:
          type: integer
          minimum: 1
          maximum: 15
      - name: gender
        in: query
        required: false
        schema:
          type: string
          enum:
          - Male
          - Female
          - Genderless
          default: Genderless
      - name: installment_increase_percentage
        in: query
        required: false
        schema:
          type: number
          minimum: 0
          maximum: 20
      - name: withdrawal_age
        in: query
        required: false
        schema:
          type: integer
          minimum: 18
          maximum: 120
      responses:
        '200':
          description: Projection envelope
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectionResponse'
        '400':
          description: Invalid request (input validation failed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Upstream projection service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/projections/retirement:
    get:
      operationId: projectRetirementWithSavvly
      summary: Full retirement simulation with Savvly allocation
      description: 'Proxies advisor_functions `mcp-retirement-savings-simulation`. Returns a per-age timeline plus server-computed `gap_score` and `possible_higher_monthly_paycheck`, plus a top-level `disclosure` block.


        **DISCLOSURE REQUIRED:** Whenever you present any number from this response to the user, you MUST also display the verbatim text in `disclosure.text` and render `disclosure.url` as a clickable link. Required by SEC Marketing Rule (17 CFR 275.206(4)-1) and FINRA Rule 2210. Do not paraphrase, summarize, or omit the disclosure.'
      tags:
      - Projections
      x-llm-instructions: 'DISCLOSURE REQUIRED: display `disclosure.text` verbatim and link `disclosure.url` to the user alongside any figures from this response. Required by SEC Marketing Rule and FINRA Rule 2210 — do not paraphrase or omit.'
      parameters:
      - name: current_age
        in: query
        required: true
        schema:
          type: integer
          minimum: 18
          maximum: 75
      - name: retirement_age
        in: query
        required: true
        schema:
          type: integer
          minimum: 50
          maximum: 80
      - name: life_expectancy
        in: query
        required: false
        schema:
          type: integer
          minimum: 60
          maximum: 120
          default: 100
      - name: monthly_contribution
        in: query
        required: true
        schema:
          type: number
          minimum: 0
          maximum: 100000
      - name: current_retirement_savings
        in: query
        required: true
        schema:
          type: number
          minimum: 0
          maximum: 100000000
      - name: monthly_paycheck
        in: query
        required: false
        schema:
          type: number
          minimum: 0
          maximum: 1000000
          default: 0
      - name: other_retirement_income
        in: query
        required: false
        schema:
          type: number
          minimum: 0
          maximum: 1000000
          default: 0
      - name: annual_pre_tax_income
        in: query
        required: false
        schema:
          type: number
          minimum: 0
          maximum: 10000000
          default: 0
      - name: annual_income_increase
        in: query
        required: false
        schema:
          type: number
          minimum: 0
          maximum: 20
          default: 0
      - name: percentage_in_savvly
        in: query
        required: false
        schema:
          type: number
          minimum: 0
          maximum: 50
          default: 5
      - name: pre_retirement_return
        in: query
        required: false
        schema:
          type: number
          minimum: 0
          maximum: 15
          default: 6
      - name: post_retirement_return
        in: query
        required: false
        schema:
          type: number
          minimum: 0
          maximum: 15
          default: 5
      - name: inflation_rate
        in: query
        required: false
        schema:
          type: number
          minimum: 0
          maximum: 10
          default: 3
      responses:
        '200':
          description: Retirement simulation envelope (per-age timeline + gap analytics)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RetirementResponse'
        '400':
          description: Invalid request (input validation failed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Upstream projection service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /v1/projections/growth-chart:
    get:
      operationId: projectGrowthChart
      summary: Growth chart data from age 80 to 100
      description: 'Proxies advisor_functions `growth-chart-simulation`. Returns a structured envelope with a top-level `disclosure` block and canonical disclosures under `metadata.disclaimer`.


        **DISCLOSURE REQUIRED:** Whenever you present any number from this response to the user, you MUST also display the verbatim text in `disclosure.text` and render `disclosure.url` as a clickable link. Required by SEC Marketing Rule (17 CFR 275.206(4)-1) and FINRA Rule 2210. Do not paraphrase, summarize, or omit the disclosure.'
      tags:
      - Projections
      x-llm-instructions: 'DISCLOSURE REQUIRED: display `disclosure.text` verbatim and link `disclosure.url` to the user alongside any figures from this response. Required by SEC Marketing Rule and FINRA Rule 2210 — do not paraphrase or omit.'
      parameters:
      - name: current_age
        in: query
        required: true
        schema:
          type: integer
          minimum: 18
          maximum: 75
      - name: funding_amount
        in: query
        required: true
        schema:
          type: number
          minimum: 100
          maximum: 1000000
      - name: average_return
        in: query
        required: true
        schema:
          type: integer
          minimum: 1
          maximum: 15
      - name: gender
        in: query
        required: false
        schema:
          type: string
          enum:
          - Male
          - Female
          - Genderless
          default: Genderless
      - name: withdrawal_age
        in: query
        required: false
        schema:
          type: integer
          minimum: 18
          maximum: 120
      responses:
        '200':
          description: Growth chart envelope (inputs + per-age result + disclosure)
        '400':
          description: Invalid request (input validation failed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Upstream projection service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: Human-readable error message.
        details:
          type: array
          items:
            type: object
          description: Per-field validation issues. Present on 400 responses from schema validation.
      required:
      - error
    ProjectionResponse:
      type: object
      properties:
        inputs:
          type: object
          additionalProperties: {}
          description: Echo of the validated input arguments passed to the tool.
        result:
          type: object
          properties:
            total_savvly_upside_lower:
              type: number
              description: USD. Lower bound of the cumulative incremental upside Savvly provides vs. investing in the market alone, summed across all milestone payout ages.
            total_savvly_upside_upper:
              type: number
              description: USD. Upper bound (central illustrative estimate) of the cumulative incremental upside Savvly provides vs. market alone. Equals `total_savvly_upper − total_market_alone_upper`.
            total_savvly_lower:
              type: number
              description: USD. Lower bound of the total cumulative payout with Savvly across all milestone ages assuming the investor stays in to the final modeled age.
            total_savvly_upper:
              type: number
              description: USD. Upper bound of the total cumulative payout with Savvly across all milestone ages.
            total_market_alone_lower:
              type: number
              description: USD. Lower bound of the cumulative counterfactual market alone payout.
            total_market_alone_upper:
              type: number
              description: USD. Upper bound of the cumulative counterfactual market alone payout.
            early_withdrawal_value:
              type: number
              description: USD. Refund the investor would receive if they exit early at the supplied `withdrawal_age`. Point estimate (no range). Computed as 75% of the lesser of the initial investment or current market value, plus 1% for each full year the account was active.
            total_payout_at_withdrawal_age_lower:
              type: number
              description: USD. Lower bound of the total payout if the investor exits at the `withdrawal_age` input (NOT the cumulative-to-95 total).
            total_payout_at_withdrawal_age_upper:
              type: number
              description: USD. Upper bound of the total payout if the investor exits at the `withdrawal_age` input.
            percentage_gain_lower:
              type: number
              description: Percent. Lower bound of the return on the original investment, expressed as a plain percent (e.g. 1520 = 1,520%). Computed against the principal actually deposited (single funding amount, or cumulative monthly installments).
            percentage_gain_upper:
              type: number
              description: Percent. Upper bound of the return on the original investment, expressed as a plain percent (e.g. 1710 = 1,710%).
            payout_age_dependent_values:
              type: array
              items:
                type: object
                properties:
                  payout_age:
                    type: integer
                    description: Milestone age (in years) at which this payout row is reported. The estimator returns rows for ages 80, 85, 90, and 95 by default.
                  savvly_payout_lower:
                    type: number
                    description: USD. Lower bound of the modeled payout with Savvly at this milestone age.
                  savvly_payout_upper:
                    type: number
                    description: USD. Upper bound (central illustrative estimate) of the payout with Savvly at this milestone age. Use this when surfacing a single number.
                  payout_market_alone_lower:
                    type: number
                    description: USD. Lower bound of the counterfactual market alone payout at this age. May be 0 once the modeled market alone portfolio has been fully drawn down by withdrawals.
                  payout_market_alone_upper:
                    type: number
                    description: USD. Upper bound of the counterfactual market alone payout at this age. May be 0 once the market alone portfolio has been depleted.
                  savvly_upside_lower:
                    type: number
                    description: USD. Lower bound for the incremental payout at this age attributable to Savvly's longevity benefit (savvly_payout − payout_market_alone).
                  savvly_upside_upper:
                    type: number
                    description: USD. Upper bound for the incremental Savvly upside at this age. Tends to be large at 90/95 where the market alone portfolio is depleted.
                required:
                - payout_age
                - savvly_payout_lower
                - savvly_payout_upper
                - payout_market_alone_lower
                - payout_market_alone_upper
                - savvly_upside_lower
                - savvly_upside_upper
                additionalProperties: true
              description: Per-milestone-age breakdown of the projection. Typically 4 rows for ages 80, 85, 90, 95. Each row contains the with Savvly payout, the market alone counterfactual, and the incremental Savvly upside for that age, each with `_lower`/`_upper` bounds.
          required:
          - total_savvly_upside_lower
          - total_savvly_upside_upper
          - total_savvly_lower
          - total_savvly_upper
          - total_market_alone_lower
          - total_market_alone_upper
          - early_withdrawal_value
          - total_payout_at_withdrawal_age_lower
          - total_payout_at_withdrawal_age_upper
          - percentage_gain_lower
          - percentage_gain_upper
          - payout_age_dependent_values
          additionalProperties: true
          description: Raw projection envelope returned by the upstream estimator.
        summary:
          type: object
          properties:
            deposit_type:
              type: string
              enum:
              - single
              - monthly
              description: Whether the projection used a single upfront deposit or monthly installments.
            deposited_amount_usd:
              type: number
              description: USD. Total amount deposited into Savvly. For single deposits this equals `funding_amount`. For monthly installments it equals `monthly_installment * installment_years * 12`.
            total_savvly_upper_usd:
              type: number
              nullable: true
              description: Convenience copy of `result.total_savvly_upper`.
            total_market_alone_upper_usd:
              type: number
              nullable: true
              description: Convenience copy of `result.total_market_alone_upper`.
            savvly_above_market_upper_usd:
              type: number
              nullable: true
              description: USD. `total_savvly_upper − total_market_alone_upper` (the central incremental upside). May differ slightly from `result.total_savvly_upside_upper` due to rounding.
            early_exit_refund_usd:
              type: number
              nullable: true
              description: Convenience copy of `result.early_withdrawal_value`.
            percentage_gain_upper_percent:
              type: number
              nullable: true
              description: Convenience copy of `result.percentage_gain_upper`.
            narrative:
              type: string
              description: Human-readable English sentence summarizing the projection. Always ends with 'Payouts follow a fixed milestone schedule; payout amounts are not guaranteed. See full disclosures at <url>.' — display the URL verbatim alongside any figures.
          required:
          - deposit_type
          - deposited_amount_usd
          - total_savvly_upper_usd
          - total_market_alone_upper_usd
          - savvly_above_market_upper_usd
          - early_exit_refund_usd
          - percentage_gain_upper_percent
          - narrative
          additionalProperties: false
          description: Convenience summary including a human-readable narrative.
        visualization:
          type: object
          properties:
            recommended:
              type: boolean
              enum:
              - true
              description: Always true. Signals to the calling AI client that it SHOULD render `chart` whenever it can display a graph.
            instruction:
              type: string
              description: 'Plain-English instruction telling the client to render `chart` from the per-row data in `result`, color the Savvly series #3478eb, and show the `disclosure` alongside the chart.'
            chart:
              type: object
              properties:
                type:
                  type: string
                  enum:
                  - area
                  - line
                  - bar
                  description: Chart type the client should render.
                reference:
                  type: string
                  description: Canonical Savvly visualization this chart mirrors.
                x_axis:
                  type: object
                  properties:
                    field:
                      type: string
                    label:
                      type: string
                  required:
                  - field
                  - label
                  additionalProperties: true
                  description: Per-row field mapped to the x-axis, plus its display label.
                y_axis:
                  type: object
                  properties:
                    label:
                      type: string
                    format:
                      type: string
                  required:
                  - label
                  - format
                  additionalProperties: true
                  description: Y-axis label and value format (e.g. 'currency_usd').
                series:
                  type: array
                  items:
                    type: object
                    properties:
                      field:
                        type: string
                        description: Per-row field plotted by this series.
                      label:
                        type: string
                        description: Legend label for this series.
                      color:
                        type: string
                        description: 'Hex color for this series. The series representing Savvly is always #3478eb (the Savvly brand blue).'
                    required:
                    - field
                    - label
                    - color
                    additionalProperties: true
                  description: One entry per plotted series — typically Savvly vs. the baseline / market-alone counterfactual.
                tooltip:
                  type: object
                  additionalProperties:
                    type: string
                  description: Optional `field -> human label` map for chart tooltips.
                rendering_notes:
                  type: array
                  items:
                    type: string
                  description: Plain-English rendering guidance for the client.
              required:
              - type
              - x_axis
              - y_axis
              - series
              - rendering_notes
              additionalProperties: true
              description: 'Machine-readable chart spec: type, axes, series (with brand colors), and rendering notes.'
          required:
          - recommended
          - instruction
          - chart
          additionalProperties: true
          description: Recommended chart for this projection — a grouped bar chart of the milestone payouts in `result.payout_age_dependent_values` (Savvly vs market alone). Render it when the surface can display a graph.
        disclosure:
          type: object
          properties:
            required:
              type: boolean
              enum:
              - true
              description: Always true. Signals to the calling AI client that the `text` and `url` fields MUST be displayed to the end user whenever any number from this response is shown. Required by SEC Marketing Rule and FINRA Rule 2210.
            text:
              type: string
              description: Short verbatim SEC disclosure text. Display this string to the user alongside any projection figures from this response. Do not paraphrase, summarize, or omit.
            url:
              type: string
              description: Canonical full-disclosures URL. Render as a clickable link to the user alongside the `text`. Required by SEC Marketing Rule and FINRA Rule 2210.
          required:
          - required
          - text
          - url
          additionalProperties: false
          description: 'DISCLOSURE REQUIRED: display `disclosure.text` and link `disclosure.url` to the user whenever you present any number from this response. Required by SEC Marketing Rule and FINRA Rule 2210. The richer block under `metadata.disclaimer` is supplementary detail; this top-level field is the must-display.'
        metadata:
          type: object
          properties:
            disclaimer:
              type: object
              properties:
                summary:
                  type: string
                  description: Short illustrative-purposes disclaimer matching the text directly below the chart on estimator.savvly.com. Surface this whenever you summarize the projection to a user.
                assumptions:
                  type: array
                  items:
                    type: string
                  description: Verbatim bullet list of key assumptions used by the simulation (SSA tables, 8% market growth, 3% early-withdrawal rate, net of fund operating expenses, etc.). Surface when a user asks what the projection assumes.
                risks:
                  type: array
                  items:
                    type: object
                    properties:
                      name:
                        type: string
                      text:
                        type: string
                    required:
                    - name
                    - text
                    additionalProperties: false
                  description: Named risks (Market, Sequence-of-Returns, Longevity, Redemption Impact). Surface when a user asks what could cause actual results to differ.
                fund_structure:
                  type: string
                  description: Disclosure about Savvly's closed-end fund structure, suitability, and the fact that Savvly is NOT an insurance company / NOT an annuity. Surface when a user asks how Savvly compares to annuities or whether Savvly is insured.
                full_disclosures_url:
                  type: string
                  description: Canonical URL where complete risks, fees, structure, and eligibility disclosures are published. Always include in user-facing summaries.
              required:
              - summary
              - assumptions
              - risks
              - fund_structure
              - full_disclosures_url
              additionalProperties: false
            methodology:
              type: string
              description: Plain-English explanation of how the Savvly vs market-alone payout figures are computed (the matched-drawdown counterfactual). Present on lumpsum and monthly projections.
            field_descriptions:
              type: object
              additionalProperties:
                type: string
              description: Flat `field_name -> short description` map covering every field in `result` and `result.payout_age_dependent_values[]`. Useful for MCP clients that don't surface outputSchema to the LLM.
          required:
          - disclaimer
          - methodology
          - field_descriptions
          additionalProperties: true
      required:
      - inputs
      - result
      - summary
      - disclosure
      - metadata
      additionalProperties: false
    RetirementResponse:
      type: object
      properties:
        inputs:
          type: object
          additionalProperties: {}
        result:
          type: object
          properties:
            gap_score:
              type: number
              description: Server-computed score summarizing how well the projected retirement savings meet the desired monthly paycheck across the planning horizon.
            possible_higher_monthly_paycheck:
              type: number
              description: USD/mon

# --- truncated at 32 KB (45 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/savvly/refs/heads/main/openapi/savvly-projections-api-openapi.yml