Turns a guessed number into a sampled distribution — percentiles, NPV/IRR/payback, sensitivity.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Purchases credits using a payment method already saved from a prior Stripe Checkout session. Use this when an authenticated account is running low on credits and needs more to keep submitting runs or analyses, without a human re-entering card details. See GET /billing/pricing for the current per-credit price. Example: {"credits":100}
Number of credits to purchase.
Your API key from POST /api/v1/accounts. Required unless you are authenticated via an Authorization: Bearer token.
The server marks this tool as destructive. Running it may delete or overwrite data on the server or in any account you connected with your headers.
Purchases credits using a payment method already saved from a prior Stripe Checkout session. Use this when an authenticated account is running low on credits and needs more to keep submitting runs or analyses, without a human re-entering card details. See GET /billing/pricing for the current per-credit price. Example: {"credits":100}
| Argument | Type | Description |
|---|---|---|
| api_key | any | Your API key from POST /api/v1/accounts. Required unless you are authenticated via an Authorization: Bearer token. |
| credits* | integer | Number of credits to purchase. |
{
"type": "object",
"title": "BuyCreditsInput",
"required": [
"credits"
],
"properties": {
"api_key": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Api Key",
"default": null,
"description": "Your API key from POST /api/v1/accounts. Required unless you are authenticated via an Authorization: Bearer token."
},
"credits": {
"type": "integer",
"title": "Credits",
"minimum": 1,
"description": "Number of credits to purchase."
}
},
"description": "Arguments for ``buy_credits`` (neumann#192; mirrors fermi's schema).\n\nAuthentication is required (unlike the free tools above): an anonymous\ncaller gets an ``authentication_required`` tool error. No price literal\nhere by design — see GET /billing/pricing for the current per-credit cost."
}Pre-flight-sizes a graph without running it: estimated peak memory, engine complexity, which tier (small/large) it would run as, and the credit cost. Use this when you want to know what a run will cost or how heavy it is before committing to it, especially for a large or unfamiliar graph. Free; no credits debited. Example: {"graph":{"root_nodes":[{"id":"root","distribution_type":"normal","distribution_params":{"mean":100,"std":15}}],"calc_nodes":[{"id":"out","formula":"{e1}"}],"edges":[{"id":"e1","source":"root","target":"out"}],"run_params":{"trials":1000,"time_periods":12}}}
| Argument | Type | Description |
|---|---|---|
| graph* | any | The graph to size. |
| api_key | any | Optional API key (estimation is free). |
{
"type": "object",
"$defs": {
"Edge": {
"type": "object",
"title": "Edge",
"required": [
"id",
"source",
"target"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique edge id within the graph."
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Name",
"default": null,
"description": "Optional label."
},
"delay": {
"type": "integer",
"title": "Delay",
"default": 0,
"minimum": 0,
"description": "Periods the value is delayed in transit."
},
"source": {
"type": "string",
"title": "Source",
"description": "id of the source node."
},
"target": {
"type": "string",
"title": "Target",
"description": "id of the target node."
}
},
"description": "A directed connection carrying a value from source to target.",
"additionalProperties": false
},
"CalcNode": {
"type": "object",
"title": "CalcNode",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique node id within the graph."
},
"name": {
"type": "string",
"title": "Name",
"default": "",
"description": "Human-readable label."
},
"formula": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Formula",
"default": null,
"description": "Optional expression over incoming edge ids, e.g. '{e1} * 0.5 + {e2}'. To sum incoming edges write '{e1}+{e2}'; for pass-through use '{e1}'. If omitted/empty the node is treated as a constant 0 (sum-of-incoming is not implied); validate_graph reports this as a warning."
}
},
"description": "A computed node. ``formula`` is OPTIONAL.\n\nA null/empty/whitespace formula is allowed: the node is treated as a\nconstant ``0`` every period/trial (incoming edges are ignored — there is no\nimplicit \"sum of incoming edges\"). ``validate_graph`` surfaces this as a\nWARNING, not an error. To sum incoming edges, write the sum explicitly,\ne.g. ``{e1}+{e2}``; for pass-through use ``{e1}``.",
"additionalProperties": false
},
"RootNode": {
"type": "object",
"title": "RootNode",
"required": [
"id",
"distribution_type",
"distribution_params"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique node id within the graph."
},
"name": {
"type": "string",
"title": "Name",
"default": "",
"description": "Human-readable label."
},
"delay": {
"type": "integer",
"title": "Delay",
"default": 0,
"minimum": 0,
"description": "Periods to wait before the first sample."
},
"interval": {
"type": "integer",
"title": "Interval",
"default": 1,
"minimum": 0,
"description": "Sample every N periods (1 = every period)."
},
"distribution_type": {
"type": "string",
"title": "Distribution Type",
"description": "One of the catalog distributions (GET /api/v1/contract)."
},
"sampling_frequency": {
"enum": [
"per_period",
"once"
],
"type": "string",
"title": "Sampling Frequency",
"default": "per_period",
"description": "'once' draws a single value reused across periods."
},
"distribution_params": {
"type": "object",
"title": "Distribution Params",
"description": "Params for the chosen distribution, e.g. {'mean': 100, 'std': 15}.",
"additionalProperties": true
}
},
"description": "A source node that samples from a probability distribution each period.",
"additionalProperties": false
},
"RunParams": {
"type": "object",
"title": "RunParams",
"required": [
"trials",
"time_periods"
],
"properties": {
"trials": {
"type": "integer",
"title": "Trials",
"description": "Number of Monte Carlo trials.",
"exclusiveMinimum": 0
},
"time_periods": {
"type": "integer",
"title": "Time Periods",
"description": "Number of time periods to simulate.",
"exclusiveMinimum": 0
},
"output_request": {
"$ref": "#/$defs/OutputRequest"
}
},
"description": "Top-level run configuration.",
"additionalProperties": false
},
"PublicGraph": {
"type": "object",
"title": "PublicGraph",
"required": [
"run_params"
],
"properties": {
"edges": {
"type": "array",
"items": {
"$ref": "#/$defs/Edge"
},
"title": "Edges"
},
"calc_nodes": {
"type": "array",
"items": {
"$ref": "#/$defs/CalcNode"
},
"title": "Calc Nodes"
},
"root_nodes": {
"type": "array",
"items": {
"$ref": "#/$defs/RootNode"
},
"title": "Root Nodes"
},
"run_params": {
"$ref": "#/$defs/RunParams"
},
"contract_version": {
"type": "string",
"const": "1.0",
"title": "Contract Version",
"default": "1.0",
"description": "Schema version this graph targets."
}
},
"description": "A complete, validated simulation request — the agent-facing input.\n\nThis is the canonical public shape. Reference integrity (edges point at real\nnodes, output_request references real ids) is validated here so malformed\ngraphs are rejected before they ever reach the engine.",
"additionalProperties": false
},
"OutputRequest": {
"type": "object",
"title": "OutputRequest",
"properties": {
"edges": {
"type": "array",
"items": {
"type": "string"
},
"title": "Edges"
},
"nodes": {
"type": "array",
"items": {
"type": "string"
},
"title": "Nodes"
},
"aggregations": {
"type": "array",
"items": {
"$ref": "#/$defs/AggregationRequest"
},
"title": "Aggregations"
},
"time_periods": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Time Periods"
}
},
"description": "Which nodes/edges/periods to include in the result.\n\nEmpty lists mean 'all': all nodes, all edges, all time periods.\n``aggregations`` requests post-simulation series metrics (NPV/IRR/...).",
"additionalProperties": false
},
"AggregationRequest": {
"type": "object",
"title": "AggregationRequest",
"required": [
"id",
"function"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Result key for this metric (unique within the request)."
},
"params": {
"type": "object",
"title": "Params",
"description": "Function params, e.g. {'rate': 0.1} for npv/discounted_payback, {'op': '>', 'value': 0} for prob, {'g': 0.02, 'rate': 0.1} for terminal_value, {'finance_rate': 0.1, 'reinvest_rate': 0.08} for mirr.",
"additionalProperties": true
},
"function": {
"type": "string",
"title": "Function",
"description": "One of: npv, irr, payback, discounted_payback, cagr, cumsum, prob, terminal_value, mirr. discounted_payback requires params.rate; npv requires params.rate OR a per-period params.rates curve; prob requires params.op (one of >, >=, <, <=, ==) and params.value; terminal_value requires params.g and params.rate; mirr requires params.finance_rate and params.reinvest_rate."
},
"target_node_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Target Node Id",
"default": null,
"description": "The node whose full per-period series the metric consumes. Required for every metric except a ``prob`` that targets a prior aggregation via ``target_aggregation_id``. Exactly one of ``target_node_id`` / ``target_aggregation_id`` may be set on a ``prob`` request."
},
"target_aggregation_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Target Aggregation Id",
"default": null,
"description": "For a ``prob`` request only: the id of an aggregation defined EARLIER in this output_request whose per-trial vector (e.g. an ``npv``) is thresholded. Lets ``P(NPV > 0)`` be expressed directly instead of the per-period ``P(value > x)`` you get by targeting a multi-period node. The referenced aggregation must produce a per-trial vector (npv/irr/payback/discounted_payback/cagr/cumsum/terminal_value/mirr)."
}
},
"description": "A post-simulation, series-aware metric over one node's full series.\n\nNPV/IRR/payback/CAGR/cumsum are NOT per-period node values: they consume a\ntarget node's complete ``(time_periods x trials)`` matrix AFTER the run and\nreturn one value per trial, summarised into a distribution.\n\nDiscount convention (0-based series index): period ``t=0`` is the present\n(undiscounted) cash flow; period ``t`` is discounted by ``(1+rate)**t``. The\nfirst period is undiscounted.\n\nWarning — two discount conventions coexist; do not mix them. These\naggregations use the **0-based** series index above (first period\nundiscounted). The per-period formula functions ``DISCOUNT(rate,\n{time_period})`` and ``PV(rate, value, {time_period})`` use the **1-based**\n``{time_period}``, so they discount the first period by ``(1+rate)**1``.\n\n\"Investment at period 0\" pattern: model an up-front outlay as a negative\nperiod-0 cash flow in the target node's series; NPV's t=0-undiscounted\nconvention then treats it as the present-value outlay. Do not additionally\ndiscount that first period with ``PV(...,{time_period})`` inside the target\nnode's formula when also requesting an aggregation ``npv`` — combining the\n1-based per-period discount with the 0-based ``npv`` double-shifts the first\nperiod.\n\nPer-period node vs aggregate metric (``prob``): pointing ``prob`` at a\nmulti-period cash-flow NODE via ``target_node_id`` thresholds only that node's\nFINAL-period per-trial value — i.e. ``P(per-period value > x)``, NOT\n``P(NPV > x)``. To get a true ``P(NPV > 0)``, define an ``npv`` aggregation\nfirst, then add a ``prob`` aggregation whose ``target_aggregation_id`` points\nat that ``npv`` id; ``prob`` then thresholds the NPV per-trial vector directly.\nA ``prob`` request targets EITHER a node (``target_node_id``) OR an earlier\naggregation (``target_aggregation_id``) — exactly one, never both.",
"additionalProperties": false
}
},
"title": "EstimateInput",
"required": [
"graph"
],
"properties": {
"graph": {
"$ref": "#/$defs/PublicGraph",
"description": "The graph to size."
},
"api_key": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Api Key",
"default": null,
"description": "Optional API key (estimation is free)."
}
},
"description": "Arguments for ``estimate`` (runtime/memory pre-flight)."
}Sends feedback, a feature request, or a bug report straight to the Neumann team, CC'ing your sender_email so you keep a copy. Use this when something about the service is wrong, missing, or confusing and you want a human to see it directly rather than guessing at a workaround. Anonymous submissions are allowed. Example: {"feedback_type":"feature_request","message":"Add CSV export for run results.","sender_email":"[email protected]"}
| Argument | Type | Description |
|---|---|---|
| api_key | any | Your API key from POST /api/v1/accounts (optional). If provided, your account id is attached to the message for context. |
| message* | string | Your feedback or request. Be as specific as possible. |
| sender_name | any | Your name (optional). |
| sender_email* | string | Your email address. A copy of your feedback is CC'd here so you have a record of what you submitted. |
| feedback_type* | string | The kind of feedback: 'feedback' for general comments, 'feature_request' for new capabilities, 'bug' for issues. |
{
"type": "object",
"title": "FeedbackInput",
"required": [
"feedback_type",
"message",
"sender_email"
],
"properties": {
"api_key": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Api Key",
"default": null,
"description": "Your API key from POST /api/v1/accounts (optional). If provided, your account id is attached to the message for context."
},
"message": {
"type": "string",
"title": "Message",
"maxLength": 5000,
"minLength": 10,
"description": "Your feedback or request. Be as specific as possible."
},
"sender_name": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"title": "Sender Name",
"default": null,
"description": "Your name (optional)."
},
"sender_email": {
"type": "string",
"title": "Sender Email",
"format": "email",
"description": "Your email address. A copy of your feedback is CC'd here so you have a record of what you submitted."
},
"feedback_type": {
"enum": [
"feedback",
"feature_request",
"bug"
],
"type": "string",
"title": "Feedback Type",
"description": "The kind of feedback: 'feedback' for general comments, 'feature_request' for new capabilities, 'bug' for issues."
}
},
"description": "Arguments for ``feedback`` (neumann#114; mirrors fermi's schema)."
}Fetches the status and, once available, the summarised result for a previously-submitted run by its run_id. Use this when you want to re-check or re-fetch a run you already submitted (e.g. via run_simulation) without resubmitting the graph -- run_simulation itself already blocks until done and returns the full result directly, so this is not a poll-until-ready step for it. Example: {"run_id":"run_8f3c2a"}
| Argument | Type | Description |
|---|---|---|
| run_id* | string | A run id returned by run_simulation. |
{
"type": "object",
"title": "GetResultsInput",
"required": [
"run_id"
],
"properties": {
"run_id": {
"type": "string",
"title": "Run Id",
"description": "A run id returned by run_simulation."
}
},
"description": "Arguments for ``get_results``."
}Runs a one-at-a-time tornado sensitivity analysis: sweeps each root node over a set of quantiles (others held at baseline) and measures how the target node responds, ranked by impact. Use this when you need to know which uncertain input most drives an outcome, so you know what to de-risk or estimate more carefully first. Debits one credit for authenticated callers. Example: {"graph":{"root_nodes":[{"id":"root","distribution_type":"normal","distribution_params":{"mean":100,"std":15}}],"calc_nodes":[{"id":"out","formula":"{e1}"}],"edges":[{"id":"e1","source":"root","target":"out"}],"run_params":{"trials":1000,"time_periods":12}}}
| Argument | Type | Description |
|---|---|---|
| seed | any | Optional RNG seed for reproducible perturbed runs. |
| graph* | any | The full simulation graph to analyse (same shape as run_simulation). |
| api_key | any | Your API key from POST /api/v1/accounts. Debits credits per analysis. Omit to run anonymously (free). |
| quantiles | any | Explicit quantiles to sweep each input over (0..1). Mutually exclusive with num_quantiles. Default: [0.05, 0.5, 0.95] (tornado). |
| time_period | any | 0-based time period to analyse. Defaults to the last period (time_periods - 1). |
| num_quantiles | any | Even count (2-10) of symmetrical quantiles around the 0.5 baseline. Mutually exclusive with quantiles. |
Runs a Monte Carlo simulation over a graph of probabilistic root and calculated nodes, returning the full output distribution instead of one guessed number. Use this when you need the range and likelihood of outcomes for a plan driven by uncertain inputs, not just its average case. Both tiers run to completion and return the full result in this same call, never a run_id to poll. Example: {"graph":{"root_nodes":[{"id":"root","distribution_type":"normal","distribution_params":{"mean":100,"std":15}}],"calc_nodes":[{"id":"out","formula":"{e1}"}],"edges":[{"id":"e1","source":"root","target":"out"}],"run_params":{"trials":1000,"time_periods":12}}}
| Argument | Type | Description |
|---|---|---|
| seed | any | Optional RNG seed; reproducible only on the small/inline path. |
| graph* | any | The full simulation graph (root_nodes, calc_nodes, edges, run_params). See GET /api/v1/contract for valid distribution types. |
| api_key | any | Your API key from POST /api/v1/accounts. Debits credits per run. Omit to run anonymously (free, but no credit accounting). |
| include_histogram | boolean | Include a per-element histogram in each summary element. |
{
"type": "object",
"$defs": {
"Edge": {
"type": "object",
"title": "Edge",
"required": [
"id",
"source",
"target"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique edge id within the graph."
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Name",
"default": null,
"description": "Optional label."
},
"delay": {
"type": "integer",
"title": "Delay",
"default": 0,
"minimum": 0,
"description": "Periods the value is delayed in transit."
},
"source": {
"type": "string",
"title": "Source",
"description": "id of the source node."
},
"target": {
"type": "string",
"title": "Target",
"description": "id of the target node."
}
},
"description": "A directed connection carrying a value from source to target.",
"additionalProperties": false
},
"CalcNode": {
"type": "object",
"title": "CalcNode",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique node id within the graph."
},
"name": {
"type": "string",
"title": "Name",
"default": "",
"description": "Human-readable label."
},
"formula": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Formula",
"default": null,
"description": "Optional expression over incoming edge ids, e.g. '{e1} * 0.5 + {e2}'. To sum incoming edges write '{e1}+{e2}'; for pass-through use '{e1}'. If omitted/empty the node is treated as a constant 0 (sum-of-incoming is not implied); validate_graph reports this as a warning."
}
},
"description": "A computed node. ``formula`` is OPTIONAL.\n\nA null/empty/whitespace formula is allowed: the node is treated as a\nconstant ``0`` every period/trial (incoming edges are ignored — there is no\nimplicit \"sum of incoming edges\"). ``validate_graph`` surfaces this as a\nWARNING, not an error. To sum incoming edges, write the sum explicitly,\ne.g. ``{e1}+{e2}``; for pass-through use ``{e1}``.",
"additionalProperties": false
},
"RootNode": {
"type": "object",
"title": "RootNode",
"required": [
"id",
"distribution_type",
"distribution_params"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique node id within the graph."
},
"name": {
"type": "string",
"title": "Name",
"default": "",
"description": "Human-readable label."
},
"delay": {
"type": "integer",
"title": "Delay",
"default": 0,
"minimum": 0,
"description": "Periods to wait before the first sample."
},
"interval": {
"type": "integer",
"title": "Interval",
"default": 1,
"minimum": 0,
"description": "Sample every N periods (1 = every period)."
},
"distribution_type": {
"type": "string",
"title": "Distribution Type",
"description": "One of the catalog distributions (GET /api/v1/contract)."
},
"sampling_frequency": {
"enum": [
"per_period",
"once"
],
"type": "string",
"title": "Sampling Frequency",
"default": "per_period",
"description": "'once' draws a single value reused across periods."
},
"distribution_params": {
"type": "object",
"title": "Distribution Params",
"description": "Params for the chosen distribution, e.g. {'mean': 100, 'std': 15}.",
"additionalProperties": true
}
},
"description": "A source node that samples from a probability distribution each period.",
"additionalProperties": false
},
"RunParams": {
"type": "object",
"title": "RunParams",
"required": [
"trials",
"time_periods"
],
"properties": {
"trials": {
"type": "integer",
"title": "Trials",
"description": "Number of Monte Carlo trials.",
"exclusiveMinimum": 0
},
"time_periods": {
"type": "integer",
"title": "Time Periods",
"description": "Number of time periods to simulate.",
"exclusiveMinimum": 0
},
"output_request": {
"$ref": "#/$defs/OutputRequest"
}
},
"description": "Top-level run configuration.",
"additionalProperties": false
},
"PublicGraph": {
"type": "object",
"title": "PublicGraph",
"required": [
"run_params"
],
"properties": {
"edges": {
"type": "array",
"items": {
"$ref": "#/$defs/Edge"
},
"title": "Edges"
},
"calc_nodes": {
"type": "array",
"items": {
"$ref": "#/$defs/CalcNode"
},
"title": "Calc Nodes"
},
"root_nodes": {
"type": "array",
"items": {
"$ref": "#/$defs/RootNode"
},
"title": "Root Nodes"
},
"run_params": {
"$ref": "#/$defs/RunParams"
},
"contract_version": {
"type": "string",
"const": "1.0",
"title": "Contract Version",
"default": "1.0",
"description": "Schema version this graph targets."
}
},
"description": "A complete, validated simulation request — the agent-facing input.\n\nThis is the canonical public shape. Reference integrity (edges point at real\nnodes, output_request references real ids) is validated here so malformed\ngraphs are rejected before they ever reach the engine.",
"additionalProperties": false
},
"OutputRequest": {
"type": "object",
"title": "OutputRequest",
"properties": {
"edges": {
"type": "array",
"items": {
"type": "string"
},
"title": "Edges"
},
"nodes": {
"type": "array",
"items": {
"type": "string"
},
"title": "Nodes"
},
"aggregations": {
"type": "array",
"items": {
"$ref": "#/$defs/AggregationRequest"
},
"title": "Aggregations"
},
"time_periods": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Time Periods"
}
},
"description": "Which nodes/edges/periods to include in the result.\n\nEmpty lists mean 'all': all nodes, all edges, all time periods.\n``aggregations`` requests post-simulation series metrics (NPV/IRR/...).",
"additionalProperties": false
},
"AggregationRequest": {
"type": "object",
"title": "AggregationRequest",
"required": [
"id",
"function"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Result key for this metric (unique within the request)."
},
"params": {
"type": "object",
"title": "Params",
"description": "Function params, e.g. {'rate': 0.1} for npv/discounted_payback, {'op': '>', 'value': 0} for prob, {'g': 0.02, 'rate': 0.1} for terminal_value, {'finance_rate': 0.1, 'reinvest_rate': 0.08} for mirr.",
"additionalProperties": true
},
"function": {
"type": "string",
"title": "Function",
"description": "One of: npv, irr, payback, discounted_payback, cagr, cumsum, prob, terminal_value, mirr. discounted_payback requires params.rate; npv requires params.rate OR a per-period params.rates curve; prob requires params.op (one of >, >=, <, <=, ==) and params.value; terminal_value requires params.g and params.rate; mirr requires params.finance_rate and params.reinvest_rate."
},
"target_node_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Target Node Id",
"default": null,
"description": "The node whose full per-period series the metric consumes. Required for every metric except a ``prob`` that targets a prior aggregation via ``target_aggregation_id``. Exactly one of ``target_node_id`` / ``target_aggregation_id`` may be set on a ``prob`` request."
},
"target_aggregation_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Target Aggregation Id",
"default": null,
"description": "For a ``prob`` request only: the id of an aggregation defined EARLIER in this output_request whose per-trial vector (e.g. an ``npv``) is thresholded. Lets ``P(NPV > 0)`` be expressed directly instead of the per-period ``P(value > x)`` you get by targeting a multi-period node. The referenced aggregation must produce a per-trial vector (npv/irr/payback/discounted_payback/cagr/cumsum/terminal_value/mirr)."
}
},
"description": "A post-simulation, series-aware metric over one node's full series.\n\nNPV/IRR/payback/CAGR/cumsum are NOT per-period node values: they consume a\ntarget node's complete ``(time_periods x trials)`` matrix AFTER the run and\nreturn one value per trial, summarised into a distribution.\n\nDiscount convention (0-based series index): period ``t=0`` is the present\n(undiscounted) cash flow; period ``t`` is discounted by ``(1+rate)**t``. The\nfirst period is undiscounted.\n\nWarning — two discount conventions coexist; do not mix them. These\naggregations use the **0-based** series index above (first period\nundiscounted). The per-period formula functions ``DISCOUNT(rate,\n{time_period})`` and ``PV(rate, value, {time_period})`` use the **1-based**\n``{time_period}``, so they discount the first period by ``(1+rate)**1``.\n\n\"Investment at period 0\" pattern: model an up-front outlay as a negative\nperiod-0 cash flow in the target node's series; NPV's t=0-undiscounted\nconvention then treats it as the present-value outlay. Do not additionally\ndiscount that first period with ``PV(...,{time_period})`` inside the target\nnode's formula when also requesting an aggregation ``npv`` — combining the\n1-based per-period discount with the 0-based ``npv`` double-shifts the first\nperiod.\n\nPer-period node vs aggregate metric (``prob``): pointing ``prob`` at a\nmulti-period cash-flow NODE via ``target_node_id`` thresholds only that node's\nFINAL-period per-trial value — i.e. ``P(per-period value > x)``, NOT\n``P(NPV > x)``. To get a true ``P(NPV > 0)``, define an ``npv`` aggregation\nfirst, then add a ``prob`` aggregation whose ``target_aggregation_id`` points\nat that ``npv`` id; ``prob`` then thresholds the NPV per-trial vector directly.\nA ``prob`` request targets EITHER a node (``target_node_id``) OR an earlier\naggregation (``target_aggregation_id``) — exactly one, never both.",
"additionalProperties": false
}
},
"title": "RunSimulationInput",
"required": [
"graph"
],
"properties": {
"seed": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Seed",
"default": null,
"description": "Optional RNG seed; reproducible only on the small/inline path."
},
"graph": {
"$ref": "#/$defs/PublicGraph",
"description": "The full simulation graph (root_nodes, calc_nodes, edges, run_params). See GET /api/v1/contract for valid distribution types."
},
"api_key": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Api Key",
"default": null,
"description": "Your API key from POST /api/v1/accounts. Debits credits per run. Omit to run anonymously (free, but no credit accounting)."
},
"include_histogram": {
"type": "boolean",
"title": "Include Histogram",
"default": false,
"description": "Include a per-element histogram in each summary element."
}
},
"description": "Arguments for ``run_simulation``."
}Runs a simulation once and summarises a target node's worst/median/best outcomes (any percentiles), each paired with a best-effort driving input per root node. Use this when you need a quick worst-case/median/best-case narrative for a plan rather than the full distribution. Debits one credit for authenticated callers. Example: {"graph":{"root_nodes":[{"id":"root","distribution_type":"normal","distribution_params":{"mean":100,"std":15}}],"calc_nodes":[{"id":"out","formula":"{e1}"}],"edges":[{"id":"e1","source":"root","target":"out"}],"run_params":{"trials":1000,"time_periods":12}}}
| Argument | Type | Description |
|---|---|---|
| seed | any | Optional RNG seed for a reproducible run. |
| graph* | any | The full simulation graph (same shape as run_simulation). |
| api_key | any | Your API key from POST /api/v1/accounts. Debits credits per analysis (priced like one run). Omit to run anonymously (free). |
| percentiles | any | Scenario percentiles in [0,1]. Default [0.0, 0.5, 1.0] = worst / median / best. |
| time_period | any | 0-based time period to analyse. Defaults to the last period (time_periods - 1). |
| target_node_id | any | The output node to summarise. Defaults to the graph's last calc node (the usual goal node). |
{
"type": "object",
"$defs": {
"Edge": {
"type": "object",
"title": "Edge",
"required": [
"id",
"source",
"target"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique edge id within the graph."
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Name",
"default": null,
"description": "Optional label."
},
"delay": {
"type": "integer",
"title": "Delay",
"default": 0,
"minimum": 0,
"description": "Periods the value is delayed in transit."
},
"source": {
"type": "string",
"title": "Source",
"description": "id of the source node."
},
"target": {
"type": "string",
"title": "Target",
"description": "id of the target node."
}
},
"description": "A directed connection carrying a value from source to target.",
"additionalProperties": false
},
"CalcNode": {
"type": "object",
"title": "CalcNode",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique node id within the graph."
},
"name": {
"type": "string",
"title": "Name",
"default": "",
"description": "Human-readable label."
},
"formula": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Formula",
"default": null,
"description": "Optional expression over incoming edge ids, e.g. '{e1} * 0.5 + {e2}'. To sum incoming edges write '{e1}+{e2}'; for pass-through use '{e1}'. If omitted/empty the node is treated as a constant 0 (sum-of-incoming is not implied); validate_graph reports this as a warning."
}
},
"description": "A computed node. ``formula`` is OPTIONAL.\n\nA null/empty/whitespace formula is allowed: the node is treated as a\nconstant ``0`` every period/trial (incoming edges are ignored — there is no\nimplicit \"sum of incoming edges\"). ``validate_graph`` surfaces this as a\nWARNING, not an error. To sum incoming edges, write the sum explicitly,\ne.g. ``{e1}+{e2}``; for pass-through use ``{e1}``.",
"additionalProperties": false
},
"RootNode": {
"type": "object",
"title": "RootNode",
"required": [
"id",
"distribution_type",
"distribution_params"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique node id within the graph."
},
"name": {
"type": "string",
"title": "Name",
"default": "",
"description": "Human-readable label."
},
"delay": {
"type": "integer",
"title": "Delay",
"default": 0,
"minimum": 0,
"description": "Periods to wait before the first sample."
},
"interval": {
"type": "integer",
"title": "Interval",
"default": 1,
"minimum": 0,
"description": "Sample every N periods (1 = every period)."
},
"distribution_type": {
"type": "string",
"title": "Distribution Type",
"description": "One of the catalog distributions (GET /api/v1/contract)."
},
"sampling_frequency": {
"enum": [
"per_period",
"once"
],
"type": "string",
"title": "Sampling Frequency",
"default": "per_period",
"description": "'once' draws a single value reused across periods."
},
"distribution_params": {
"type": "object",
"title": "Distribution Params",
"description": "Params for the chosen distribution, e.g. {'mean': 100, 'std': 15}.",
"additionalProperties": true
}
},
"description": "A source node that samples from a probability distribution each period.",
"additionalProperties": false
},
"RunParams": {
"type": "object",
"title": "RunParams",
"required": [
"trials",
"time_periods"
],
"properties": {
"trials": {
"type": "integer",
"title": "Trials",
"description": "Number of Monte Carlo trials.",
"exclusiveMinimum": 0
},
"time_periods": {
"type": "integer",
"title": "Time Periods",
"description": "Number of time periods to simulate.",
"exclusiveMinimum": 0
},
"output_request": {
"$ref": "#/$defs/OutputRequest"
}
},
"description": "Top-level run configuration.",
"additionalProperties": false
},
"PublicGraph": {
"type": "object",
"title": "PublicGraph",
"required": [
"run_params"
],
"properties": {
"edges": {
"type": "array",
"items": {
"$ref": "#/$defs/Edge"
},
"title": "Edges"
},
"calc_nodes": {
"type": "array",
"items": {
"$ref": "#/$defs/CalcNode"
},
"title": "Calc Nodes"
},
"root_nodes": {
"type": "array",
"items": {
"$ref": "#/$defs/RootNode"
},
"title": "Root Nodes"
},
"run_params": {
"$ref": "#/$defs/RunParams"
},
"contract_version": {
"type": "string",
"const": "1.0",
"title": "Contract Version",
"default": "1.0",
"description": "Schema version this graph targets."
}
},
"description": "A complete, validated simulation request — the agent-facing input.\n\nThis is the canonical public shape. Reference integrity (edges point at real\nnodes, output_request references real ids) is validated here so malformed\ngraphs are rejected before they ever reach the engine.",
"additionalProperties": false
},
"OutputRequest": {
"type": "object",
"title": "OutputRequest",
"properties": {
"edges": {
"type": "array",
"items": {
"type": "string"
},
"title": "Edges"
},
"nodes": {
"type": "array",
"items": {
"type": "string"
},
"title": "Nodes"
},
"aggregations": {
"type": "array",
"items": {
"$ref": "#/$defs/AggregationRequest"
},
"title": "Aggregations"
},
"time_periods": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Time Periods"
}
},
"description": "Which nodes/edges/periods to include in the result.\n\nEmpty lists mean 'all': all nodes, all edges, all time periods.\n``aggregations`` requests post-simulation series metrics (NPV/IRR/...).",
"additionalProperties": false
},
"AggregationRequest": {
"type": "object",
"title": "AggregationRequest",
"required": [
"id",
"function"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Result key for this metric (unique within the request)."
},
"params": {
"type": "object",
"title": "Params",
"description": "Function params, e.g. {'rate': 0.1} for npv/discounted_payback, {'op': '>', 'value': 0} for prob, {'g': 0.02, 'rate': 0.1} for terminal_value, {'finance_rate': 0.1, 'reinvest_rate': 0.08} for mirr.",
"additionalProperties": true
},
"function": {
"type": "string",
"title": "Function",
"description": "One of: npv, irr, payback, discounted_payback, cagr, cumsum, prob, terminal_value, mirr. discounted_payback requires params.rate; npv requires params.rate OR a per-period params.rates curve; prob requires params.op (one of >, >=, <, <=, ==) and params.value; terminal_value requires params.g and params.rate; mirr requires params.finance_rate and params.reinvest_rate."
},
"target_node_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Target Node Id",
"default": null,
"description": "The node whose full per-period series the metric consumes. Required for every metric except a ``prob`` that targets a prior aggregation via ``target_aggregation_id``. Exactly one of ``target_node_id`` / ``target_aggregation_id`` may be set on a ``prob`` request."
},
"target_aggregation_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Target Aggregation Id",
"default": null,
"description": "For a ``prob`` request only: the id of an aggregation defined EARLIER in this output_request whose per-trial vector (e.g. an ``npv``) is thresholded. Lets ``P(NPV > 0)`` be expressed directly instead of the per-period ``P(value > x)`` you get by targeting a multi-period node. The referenced aggregation must produce a per-trial vector (npv/irr/payback/discounted_payback/cagr/cumsum/terminal_value/mirr)."
}
},
"description": "A post-simulation, series-aware metric over one node's full series.\n\nNPV/IRR/payback/CAGR/cumsum are NOT per-period node values: they consume a\ntarget node's complete ``(time_periods x trials)`` matrix AFTER the run and\nreturn one value per trial, summarised into a distribution.\n\nDiscount convention (0-based series index): period ``t=0`` is the present\n(undiscounted) cash flow; period ``t`` is discounted by ``(1+rate)**t``. The\nfirst period is undiscounted.\n\nWarning — two discount conventions coexist; do not mix them. These\naggregations use the **0-based** series index above (first period\nundiscounted). The per-period formula functions ``DISCOUNT(rate,\n{time_period})`` and ``PV(rate, value, {time_period})`` use the **1-based**\n``{time_period}``, so they discount the first period by ``(1+rate)**1``.\n\n\"Investment at period 0\" pattern: model an up-front outlay as a negative\nperiod-0 cash flow in the target node's series; NPV's t=0-undiscounted\nconvention then treats it as the present-value outlay. Do not additionally\ndiscount that first period with ``PV(...,{time_period})`` inside the target\nnode's formula when also requesting an aggregation ``npv`` — combining the\n1-based per-period discount with the 0-based ``npv`` double-shifts the first\nperiod.\n\nPer-period node vs aggregate metric (``prob``): pointing ``prob`` at a\nmulti-period cash-flow NODE via ``target_node_id`` thresholds only that node's\nFINAL-period per-trial value — i.e. ``P(per-period value > x)``, NOT\n``P(NPV > x)``. To get a true ``P(NPV > 0)``, define an ``npv`` aggregation\nfirst, then add a ``prob`` aggregation whose ``target_aggregation_id`` points\nat that ``npv`` id; ``prob`` then thresholds the NPV per-trial vector directly.\nA ``prob`` request targets EITHER a node (``target_node_id``) OR an earlier\naggregation (``target_aggregation_id``) — exactly one, never both.",
"additionalProperties": false
}
},
"title": "ScenarioAnalysisInput",
"required": [
"graph"
],
"properties": {
"seed": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Seed",
"default": null,
"description": "Optional RNG seed for a reproducible run."
},
"graph": {
"$ref": "#/$defs/PublicGraph",
"description": "The full simulation graph (same shape as run_simulation)."
},
"api_key": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Api Key",
"default": null,
"description": "Your API key from POST /api/v1/accounts. Debits credits per analysis (priced like one run). Omit to run anonymously (free)."
},
"percentiles": {
"anyOf": [
{
"type": "array",
"items": {
"type": "number"
}
},
{
"type": "null"
}
],
"title": "Percentiles",
"default": null,
"description": "Scenario percentiles in [0,1]. Default [0.0, 0.5, 1.0] = worst / median / best."
},
"time_period": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Time Period",
"default": null,
"description": "0-based time period to analyse. Defaults to the last period (time_periods - 1)."
},
"target_node_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Target Node Id",
"default": null,
"description": "The output node to summarise. Defaults to the graph's last calc node (the usual goal node)."
}
},
"description": "Arguments for ``scenario_analysis`` (best/median/worst across trials)."
}Creates (or fetches the existing) public share link for a PERSISTED run from the stored-graph flow, renderable as HTML/JSON/SVG. Use this when you want to hand a result to someone without giving them API access -- only for a run saved via the stored-graph flow: a run_simulation result is ephemeral and has no usable run_id. Requires authentication (api_key argument or an Authorization: Bearer token) to CREATE the link; the resulting share_url itself needs no credential to view. Example: {"run_id":"run_8f3c2a"}
| Argument | Type | Description |
|---|---|---|
| run_id* | string | A persisted (stored-graph) run id. |
| api_key | any | Your API key from POST /api/v1/accounts. Required unless you are authenticated via an Authorization: Bearer token. |
{
"type": "object",
"title": "ShareResultInput",
"required": [
"run_id"
],
"properties": {
"run_id": {
"type": "string",
"title": "Run Id",
"description": "A persisted (stored-graph) run id."
},
"api_key": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Api Key",
"default": null,
"description": "Your API key from POST /api/v1/accounts. Required unless you are authenticated via an Authorization: Bearer token."
}
},
"description": "Arguments for ``share_result`` (neumann#228).\n\n``run_id`` must be a PERSISTED run from the stored-graph flow (the one\n``POST /api/v1/graphs/{graph_id}/runs`` creates), not a ``run_simulation``\nresult — those are ephemeral and were never written to durable storage, so\nthere is nothing to share (see the tool description for why)."
}Converts a human-style estimate (a min/max range at a stated confidence, or a most-likely value) into the distribution_params a root node needs. Use this when you have a rough verbal estimate like 'between 80 and 120, 90% confident' and need engine-ready parameters rather than guessing them yourself. Free; no credits debited. Example: {"distribution_type":"normal","min_estimate":80,"max_estimate":120,"percentile_range":0.9}
| Argument | Type | Description |
|---|---|---|
| api_key | any | Optional API key (solving is free). |
| max_estimate | any | High end of the range (required for most types). |
| min_estimate | any | Low end of the range (required for most types). |
| mode_estimate | any | Most-likely value; required for constant/bernoulli/binomial. |
| percentile_range | number | Confidence the [min,max] interval covers, e.g. 0.9 for 90%. |
| distribution_type* | string | A catalog distribution, e.g. 'normal', 'triangle', 'beta'. |
{
"type": "object",
"title": "SolveDistributionInput",
"required": [
"distribution_type"
],
"properties": {
"api_key": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Api Key",
"default": null,
"description": "Optional API key (solving is free)."
},
"max_estimate": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Max Estimate",
"default": null,
"description": "High end of the range (required for most types)."
},
"min_estimate": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Min Estimate",
"default": null,
"description": "Low end of the range (required for most types)."
},
"mode_estimate": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Mode Estimate",
"default": null,
"description": "Most-likely value; required for constant/bernoulli/binomial."
},
"percentile_range": {
"type": "number",
"title": "Percentile Range",
"default": 0.9,
"description": "Confidence the [min,max] interval covers, e.g. 0.9 for 90%.",
"exclusiveMaximum": 1,
"exclusiveMinimum": 0
},
"distribution_type": {
"type": "string",
"title": "Distribution Type",
"description": "A catalog distribution, e.g. 'normal', 'triangle', 'beta'."
}
},
"description": "Arguments for ``solve_distribution_parameters``.\n\nFind distribution parameters that fit a min/max range at a given confidence,\nso an agent can turn a human-style 'between X and Y' estimate into engine-ready\n``distribution_params``."
}Checks a graph for structural problems (cycles, dangling edges, unknown distributions, missing refs) before you run it, returning a list of issues by rule and severity. Use this when you built or edited a graph by hand and want to catch mistakes before spending a run on it. Free; no credits debited. Example: {"graph":{"root_nodes":[{"id":"root","distribution_type":"normal","distribution_params":{"mean":100,"std":15}}],"calc_nodes":[{"id":"out","formula":"{e1}"}],"edges":[{"id":"e1","source":"root","target":"out"}],"run_params":{"trials":1000,"time_periods":12}}}
| Argument | Type | Description |
|---|---|---|
| graph* | any | The graph to validate. |
| api_key | any | Optional API key (validation is free). |
{
"type": "object",
"$defs": {
"Edge": {
"type": "object",
"title": "Edge",
"required": [
"id",
"source",
"target"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique edge id within the graph."
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Name",
"default": null,
"description": "Optional label."
},
"delay": {
"type": "integer",
"title": "Delay",
"default": 0,
"minimum": 0,
"description": "Periods the value is delayed in transit."
},
"source": {
"type": "string",
"title": "Source",
"description": "id of the source node."
},
"target": {
"type": "string",
"title": "Target",
"description": "id of the target node."
}
},
"description": "A directed connection carrying a value from source to target.",
"additionalProperties": false
},
"CalcNode": {
"type": "object",
"title": "CalcNode",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique node id within the graph."
},
"name": {
"type": "string",
"title": "Name",
"default": "",
"description": "Human-readable label."
},
"formula": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Formula",
"default": null,
"description": "Optional expression over incoming edge ids, e.g. '{e1} * 0.5 + {e2}'. To sum incoming edges write '{e1}+{e2}'; for pass-through use '{e1}'. If omitted/empty the node is treated as a constant 0 (sum-of-incoming is not implied); validate_graph reports this as a warning."
}
},
"description": "A computed node. ``formula`` is OPTIONAL.\n\nA null/empty/whitespace formula is allowed: the node is treated as a\nconstant ``0`` every period/trial (incoming edges are ignored — there is no\nimplicit \"sum of incoming edges\"). ``validate_graph`` surfaces this as a\nWARNING, not an error. To sum incoming edges, write the sum explicitly,\ne.g. ``{e1}+{e2}``; for pass-through use ``{e1}``.",
"additionalProperties": false
},
"RootNode": {
"type": "object",
"title": "RootNode",
"required": [
"id",
"distribution_type",
"distribution_params"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique node id within the graph."
},
"name": {
"type": "string",
"title": "Name",
"default": "",
"description": "Human-readable label."
},
"delay": {
"type": "integer",
"title": "Delay",
"default": 0,
"minimum": 0,
"description": "Periods to wait before the first sample."
},
"interval": {
"type": "integer",
"title": "Interval",
"default": 1,
"minimum": 0,
"description": "Sample every N periods (1 = every period)."
},
"distribution_type": {
"type": "string",
"title": "Distribution Type",
"description": "One of the catalog distributions (GET /api/v1/contract)."
},
"sampling_frequency": {
"enum": [
"per_period",
"once"
],
"type": "string",
"title": "Sampling Frequency",
"default": "per_period",
"description": "'once' draws a single value reused across periods."
},
"distribution_params": {
"type": "object",
"title": "Distribution Params",
"description": "Params for the chosen distribution, e.g. {'mean': 100, 'std': 15}.",
"additionalProperties": true
}
},
"description": "A source node that samples from a probability distribution each period.",
"additionalProperties": false
},
"RunParams": {
"type": "object",
"title": "RunParams",
"required": [
"trials",
"time_periods"
],
"properties": {
"trials": {
"type": "integer",
"title": "Trials",
"description": "Number of Monte Carlo trials.",
"exclusiveMinimum": 0
},
"time_periods": {
"type": "integer",
"title": "Time Periods",
"description": "Number of time periods to simulate.",
"exclusiveMinimum": 0
},
"output_request": {
"$ref": "#/$defs/OutputRequest"
}
},
"description": "Top-level run configuration.",
"additionalProperties": false
},
"PublicGraph": {
"type": "object",
"title": "PublicGraph",
"required": [
"run_params"
],
"properties": {
"edges": {
"type": "array",
"items": {
"$ref": "#/$defs/Edge"
},
"title": "Edges"
},
"calc_nodes": {
"type": "array",
"items": {
"$ref": "#/$defs/CalcNode"
},
"title": "Calc Nodes"
},
"root_nodes": {
"type": "array",
"items": {
"$ref": "#/$defs/RootNode"
},
"title": "Root Nodes"
},
"run_params": {
"$ref": "#/$defs/RunParams"
},
"contract_version": {
"type": "string",
"const": "1.0",
"title": "Contract Version",
"default": "1.0",
"description": "Schema version this graph targets."
}
},
"description": "A complete, validated simulation request — the agent-facing input.\n\nThis is the canonical public shape. Reference integrity (edges point at real\nnodes, output_request references real ids) is validated here so malformed\ngraphs are rejected before they ever reach the engine.",
"additionalProperties": false
},
"OutputRequest": {
"type": "object",
"title": "OutputRequest",
"properties": {
"edges": {
"type": "array",
"items": {
"type": "string"
},
"title": "Edges"
},
"nodes": {
"type": "array",
"items": {
"type": "string"
},
"title": "Nodes"
},
"aggregations": {
"type": "array",
"items": {
"$ref": "#/$defs/AggregationRequest"
},
"title": "Aggregations"
},
"time_periods": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Time Periods"
}
},
"description": "Which nodes/edges/periods to include in the result.\n\nEmpty lists mean 'all': all nodes, all edges, all time periods.\n``aggregations`` requests post-simulation series metrics (NPV/IRR/...).",
"additionalProperties": false
},
"AggregationRequest": {
"type": "object",
"title": "AggregationRequest",
"required": [
"id",
"function"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Result key for this metric (unique within the request)."
},
"params": {
"type": "object",
"title": "Params",
"description": "Function params, e.g. {'rate': 0.1} for npv/discounted_payback, {'op': '>', 'value': 0} for prob, {'g': 0.02, 'rate': 0.1} for terminal_value, {'finance_rate': 0.1, 'reinvest_rate': 0.08} for mirr.",
"additionalProperties": true
},
"function": {
"type": "string",
"title": "Function",
"description": "One of: npv, irr, payback, discounted_payback, cagr, cumsum, prob, terminal_value, mirr. discounted_payback requires params.rate; npv requires params.rate OR a per-period params.rates curve; prob requires params.op (one of >, >=, <, <=, ==) and params.value; terminal_value requires params.g and params.rate; mirr requires params.finance_rate and params.reinvest_rate."
},
"target_node_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Target Node Id",
"default": null,
"description": "The node whose full per-period series the metric consumes. Required for every metric except a ``prob`` that targets a prior aggregation via ``target_aggregation_id``. Exactly one of ``target_node_id`` / ``target_aggregation_id`` may be set on a ``prob`` request."
},
"target_aggregation_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Target Aggregation Id",
"default": null,
"description": "For a ``prob`` request only: the id of an aggregation defined EARLIER in this output_request whose per-trial vector (e.g. an ``npv``) is thresholded. Lets ``P(NPV > 0)`` be expressed directly instead of the per-period ``P(value > x)`` you get by targeting a multi-period node. The referenced aggregation must produce a per-trial vector (npv/irr/payback/discounted_payback/cagr/cumsum/terminal_value/mirr)."
}
},
"description": "A post-simulation, series-aware metric over one node's full series.\n\nNPV/IRR/payback/CAGR/cumsum are NOT per-period node values: they consume a\ntarget node's complete ``(time_periods x trials)`` matrix AFTER the run and\nreturn one value per trial, summarised into a distribution.\n\nDiscount convention (0-based series index): period ``t=0`` is the present\n(undiscounted) cash flow; period ``t`` is discounted by ``(1+rate)**t``. The\nfirst period is undiscounted.\n\nWarning — two discount conventions coexist; do not mix them. These\naggregations use the **0-based** series index above (first period\nundiscounted). The per-period formula functions ``DISCOUNT(rate,\n{time_period})`` and ``PV(rate, value, {time_period})`` use the **1-based**\n``{time_period}``, so they discount the first period by ``(1+rate)**1``.\n\n\"Investment at period 0\" pattern: model an up-front outlay as a negative\nperiod-0 cash flow in the target node's series; NPV's t=0-undiscounted\nconvention then treats it as the present-value outlay. Do not additionally\ndiscount that first period with ``PV(...,{time_period})`` inside the target\nnode's formula when also requesting an aggregation ``npv`` — combining the\n1-based per-period discount with the 0-based ``npv`` double-shifts the first\nperiod.\n\nPer-period node vs aggregate metric (``prob``): pointing ``prob`` at a\nmulti-period cash-flow NODE via ``target_node_id`` thresholds only that node's\nFINAL-period per-trial value — i.e. ``P(per-period value > x)``, NOT\n``P(NPV > x)``. To get a true ``P(NPV > 0)``, define an ``npv`` aggregation\nfirst, then add a ``prob`` aggregation whose ``target_aggregation_id`` points\nat that ``npv`` id; ``prob`` then thresholds the NPV per-trial vector directly.\nA ``prob`` request targets EITHER a node (``target_node_id``) OR an earlier\naggregation (``target_aggregation_id``) — exactly one, never both.",
"additionalProperties": false
}
},
"title": "ValidateGraphInput",
"required": [
"graph"
],
"properties": {
"graph": {
"$ref": "#/$defs/PublicGraph",
"description": "The graph to validate."
},
"api_key": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Api Key",
"default": null,
"description": "Optional API key (validation is free)."
}
},
"description": "Arguments for ``validate_graph``."
}| target_node_id |
| any |
| The output node to measure. Defaults to the graph's last calc node (the usual goal node). |
{
"type": "object",
"$defs": {
"Edge": {
"type": "object",
"title": "Edge",
"required": [
"id",
"source",
"target"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique edge id within the graph."
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Name",
"default": null,
"description": "Optional label."
},
"delay": {
"type": "integer",
"title": "Delay",
"default": 0,
"minimum": 0,
"description": "Periods the value is delayed in transit."
},
"source": {
"type": "string",
"title": "Source",
"description": "id of the source node."
},
"target": {
"type": "string",
"title": "Target",
"description": "id of the target node."
}
},
"description": "A directed connection carrying a value from source to target.",
"additionalProperties": false
},
"CalcNode": {
"type": "object",
"title": "CalcNode",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique node id within the graph."
},
"name": {
"type": "string",
"title": "Name",
"default": "",
"description": "Human-readable label."
},
"formula": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Formula",
"default": null,
"description": "Optional expression over incoming edge ids, e.g. '{e1} * 0.5 + {e2}'. To sum incoming edges write '{e1}+{e2}'; for pass-through use '{e1}'. If omitted/empty the node is treated as a constant 0 (sum-of-incoming is not implied); validate_graph reports this as a warning."
}
},
"description": "A computed node. ``formula`` is OPTIONAL.\n\nA null/empty/whitespace formula is allowed: the node is treated as a\nconstant ``0`` every period/trial (incoming edges are ignored — there is no\nimplicit \"sum of incoming edges\"). ``validate_graph`` surfaces this as a\nWARNING, not an error. To sum incoming edges, write the sum explicitly,\ne.g. ``{e1}+{e2}``; for pass-through use ``{e1}``.",
"additionalProperties": false
},
"RootNode": {
"type": "object",
"title": "RootNode",
"required": [
"id",
"distribution_type",
"distribution_params"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Unique node id within the graph."
},
"name": {
"type": "string",
"title": "Name",
"default": "",
"description": "Human-readable label."
},
"delay": {
"type": "integer",
"title": "Delay",
"default": 0,
"minimum": 0,
"description": "Periods to wait before the first sample."
},
"interval": {
"type": "integer",
"title": "Interval",
"default": 1,
"minimum": 0,
"description": "Sample every N periods (1 = every period)."
},
"distribution_type": {
"type": "string",
"title": "Distribution Type",
"description": "One of the catalog distributions (GET /api/v1/contract)."
},
"sampling_frequency": {
"enum": [
"per_period",
"once"
],
"type": "string",
"title": "Sampling Frequency",
"default": "per_period",
"description": "'once' draws a single value reused across periods."
},
"distribution_params": {
"type": "object",
"title": "Distribution Params",
"description": "Params for the chosen distribution, e.g. {'mean': 100, 'std': 15}.",
"additionalProperties": true
}
},
"description": "A source node that samples from a probability distribution each period.",
"additionalProperties": false
},
"RunParams": {
"type": "object",
"title": "RunParams",
"required": [
"trials",
"time_periods"
],
"properties": {
"trials": {
"type": "integer",
"title": "Trials",
"description": "Number of Monte Carlo trials.",
"exclusiveMinimum": 0
},
"time_periods": {
"type": "integer",
"title": "Time Periods",
"description": "Number of time periods to simulate.",
"exclusiveMinimum": 0
},
"output_request": {
"$ref": "#/$defs/OutputRequest"
}
},
"description": "Top-level run configuration.",
"additionalProperties": false
},
"PublicGraph": {
"type": "object",
"title": "PublicGraph",
"required": [
"run_params"
],
"properties": {
"edges": {
"type": "array",
"items": {
"$ref": "#/$defs/Edge"
},
"title": "Edges"
},
"calc_nodes": {
"type": "array",
"items": {
"$ref": "#/$defs/CalcNode"
},
"title": "Calc Nodes"
},
"root_nodes": {
"type": "array",
"items": {
"$ref": "#/$defs/RootNode"
},
"title": "Root Nodes"
},
"run_params": {
"$ref": "#/$defs/RunParams"
},
"contract_version": {
"type": "string",
"const": "1.0",
"title": "Contract Version",
"default": "1.0",
"description": "Schema version this graph targets."
}
},
"description": "A complete, validated simulation request — the agent-facing input.\n\nThis is the canonical public shape. Reference integrity (edges point at real\nnodes, output_request references real ids) is validated here so malformed\ngraphs are rejected before they ever reach the engine.",
"additionalProperties": false
},
"OutputRequest": {
"type": "object",
"title": "OutputRequest",
"properties": {
"edges": {
"type": "array",
"items": {
"type": "string"
},
"title": "Edges"
},
"nodes": {
"type": "array",
"items": {
"type": "string"
},
"title": "Nodes"
},
"aggregations": {
"type": "array",
"items": {
"$ref": "#/$defs/AggregationRequest"
},
"title": "Aggregations"
},
"time_periods": {
"type": "array",
"items": {
"type": "integer"
},
"title": "Time Periods"
}
},
"description": "Which nodes/edges/periods to include in the result.\n\nEmpty lists mean 'all': all nodes, all edges, all time periods.\n``aggregations`` requests post-simulation series metrics (NPV/IRR/...).",
"additionalProperties": false
},
"AggregationRequest": {
"type": "object",
"title": "AggregationRequest",
"required": [
"id",
"function"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Result key for this metric (unique within the request)."
},
"params": {
"type": "object",
"title": "Params",
"description": "Function params, e.g. {'rate': 0.1} for npv/discounted_payback, {'op': '>', 'value': 0} for prob, {'g': 0.02, 'rate': 0.1} for terminal_value, {'finance_rate': 0.1, 'reinvest_rate': 0.08} for mirr.",
"additionalProperties": true
},
"function": {
"type": "string",
"title": "Function",
"description": "One of: npv, irr, payback, discounted_payback, cagr, cumsum, prob, terminal_value, mirr. discounted_payback requires params.rate; npv requires params.rate OR a per-period params.rates curve; prob requires params.op (one of >, >=, <, <=, ==) and params.value; terminal_value requires params.g and params.rate; mirr requires params.finance_rate and params.reinvest_rate."
},
"target_node_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Target Node Id",
"default": null,
"description": "The node whose full per-period series the metric consumes. Required for every metric except a ``prob`` that targets a prior aggregation via ``target_aggregation_id``. Exactly one of ``target_node_id`` / ``target_aggregation_id`` may be set on a ``prob`` request."
},
"target_aggregation_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Target Aggregation Id",
"default": null,
"description": "For a ``prob`` request only: the id of an aggregation defined EARLIER in this output_request whose per-trial vector (e.g. an ``npv``) is thresholded. Lets ``P(NPV > 0)`` be expressed directly instead of the per-period ``P(value > x)`` you get by targeting a multi-period node. The referenced aggregation must produce a per-trial vector (npv/irr/payback/discounted_payback/cagr/cumsum/terminal_value/mirr)."
}
},
"description": "A post-simulation, series-aware metric over one node's full series.\n\nNPV/IRR/payback/CAGR/cumsum are NOT per-period node values: they consume a\ntarget node's complete ``(time_periods x trials)`` matrix AFTER the run and\nreturn one value per trial, summarised into a distribution.\n\nDiscount convention (0-based series index): period ``t=0`` is the present\n(undiscounted) cash flow; period ``t`` is discounted by ``(1+rate)**t``. The\nfirst period is undiscounted.\n\nWarning — two discount conventions coexist; do not mix them. These\naggregations use the **0-based** series index above (first period\nundiscounted). The per-period formula functions ``DISCOUNT(rate,\n{time_period})`` and ``PV(rate, value, {time_period})`` use the **1-based**\n``{time_period}``, so they discount the first period by ``(1+rate)**1``.\n\n\"Investment at period 0\" pattern: model an up-front outlay as a negative\nperiod-0 cash flow in the target node's series; NPV's t=0-undiscounted\nconvention then treats it as the present-value outlay. Do not additionally\ndiscount that first period with ``PV(...,{time_period})`` inside the target\nnode's formula when also requesting an aggregation ``npv`` — combining the\n1-based per-period discount with the 0-based ``npv`` double-shifts the first\nperiod.\n\nPer-period node vs aggregate metric (``prob``): pointing ``prob`` at a\nmulti-period cash-flow NODE via ``target_node_id`` thresholds only that node's\nFINAL-period per-trial value — i.e. ``P(per-period value > x)``, NOT\n``P(NPV > x)``. To get a true ``P(NPV > 0)``, define an ``npv`` aggregation\nfirst, then add a ``prob`` aggregation whose ``target_aggregation_id`` points\nat that ``npv`` id; ``prob`` then thresholds the NPV per-trial vector directly.\nA ``prob`` request targets EITHER a node (``target_node_id``) OR an earlier\naggregation (``target_aggregation_id``) — exactly one, never both.",
"additionalProperties": false
}
},
"title": "OatSensitivityInput",
"required": [
"graph"
],
"properties": {
"seed": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Seed",
"default": null,
"description": "Optional RNG seed for reproducible perturbed runs."
},
"graph": {
"$ref": "#/$defs/PublicGraph",
"description": "The full simulation graph to analyse (same shape as run_simulation)."
},
"api_key": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Api Key",
"default": null,
"description": "Your API key from POST /api/v1/accounts. Debits credits per analysis. Omit to run anonymously (free)."
},
"quantiles": {
"anyOf": [
{
"type": "array",
"items": {
"type": "number"
}
},
{
"type": "null"
}
],
"title": "Quantiles",
"default": null,
"description": "Explicit quantiles to sweep each input over (0..1). Mutually exclusive with num_quantiles. Default: [0.05, 0.5, 0.95] (tornado)."
},
"time_period": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Time Period",
"default": null,
"description": "0-based time period to analyse. Defaults to the last period (time_periods - 1)."
},
"num_quantiles": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Num Quantiles",
"default": null,
"description": "Even count (2-10) of symmetrical quantiles around the 0.5 baseline. Mutually exclusive with quantiles."
},
"target_node_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Target Node Id",
"default": null,
"description": "The output node to measure. Defaults to the graph's last calc node (the usual goal node)."
}
},
"description": "Arguments for ``oat_sensitivity`` (one-at-a-time tornado analysis)."
}