Service Overview

Harcourt Valuations produces institutional-grade Net Asset Value (NAV) reports for mining companies. Each report contains a full discounted cash flow (DCF) model for every mine or project owned by the company, a P/NAV ratio, and a two-way sensitivity table showing how NAV per share moves with metal prices and discount rates.

The core thesis: the answer is a number. Mining equity analysis should produce a specific, defensible per-share value — not a narrative. P/NAV (price to net asset value) is the primary metric used by mining-focused fund managers and sell-side analysts at major Canadian and Australian banks.

Two product tiers

Free access

Covered Companies

This page does not list them. Coverage changes as models are completed, and any list written here would be a copy that drifts from the source. Two live endpoints answer it, both free, both requiring no authentication:

EndpointAnswers
GET https://reports.harcourtvaluations.ai/catalog Every covered company: name, ticker string with all listings, primary commodity, key assets, NAV, P/NAV, model date, and the free teaser. This is the authoritative coverage list.
GET https://reports.harcourtvaluations.ai/nav-debug Which of those companies has a sensitivity matrix, and that matrix's real extent — has_sensitivity, matrix_rows (the commodity axis points) and matrix_cols (the discount rates). Keyed by the same ticker stem /nav-query/{ticker} takes. Read from the model's own exported grid, so it cannot disagree with what /nav-query will do.

Resolving a company to its API ticker. Use the key from /nav-debug directly — it is the path segment /nav-query expects. Do not derive it from the ticker string in /catalog: for Seabridge Gold that string is TSX: SEA · NYSE: SA and the API ticker is sa, not sea. The listing symbol and the API ticker are not the same identifier.

Determining a company's commodity parameter. Call /nav-query/{ticker}?rate=0.08 with no commodity parameter. The response echoes the axis under inputs and names it in defaulted_inputs, so one call tells you what the axis is. Sending the wrong parameter is also safe and self-correcting: it returns 400 naming the parameter that ticker actually takes, rather than interpolating your value against an unrelated axis.

NAV Query API

The NAV Query API returns a modelled NAV per share at caller-specified inputs. It does not re-run any live model. Instead, it reads a precomputed sensitivity matrix stored in Cloudflare R2 and performs bilinear interpolation between the nearest grid points. Response time is typically under 50ms.

Direct endpoint (free, no payment)

GET https://reports.harcourtvaluations.ai/nav-query/{ticker}?{commodity}={value}&rate={discount_rate}

No authentication required. Rate limits may apply at high volume. For production agent workflows, use the Qatom MCP tool below.

The commodity parameter name is not currently validated against the ticker's grid axis. Sending ?gold= to a silver-axis ticker will interpolate the value against the silver axis and return a plausible-looking number rather than an error. Always read the echoed inputs object in the response, which names the axis actually used, and send the parameter listed for that ticker in the coverage table above.

Parameters

ParameterTypeDescriptionApplies To
goldnumberGold price, USD per troy oz (e.g. 4000)Tickers whose grid carries a gold axis
silver / agnumberSilver price, USD per troy oz (e.g. 61)Tickers whose grid carries a silver axis
cu / coppernumberCopper price, USD per lb (e.g. 6.5)Tickers whose grid carries a copper axis
zn / zincnumberZinc price, USD per lb (e.g. 1.62)Tickers whose grid carries a zinc axis
pb / leadnumberLead price, USD per lbTickers whose grid carries a lead axis
u3o8numberUranium price, USD per lb U3O8 (e.g. 82)Tickers whose grid carries a uranium axis
rownumberGeneric axis value, accepted on any ticker with exactly one commodity axis. On a multi-axis ticker it names nothing and is refused with 400, listing the parameters to send instead — so it is a fallback, not a universal escape hatch.Single-axis tickers
ratenumberDiscount rate as a decimal (e.g. 0.08 = 8%). Omit it and the grid's own base rate is used and reported in defaulted_inputs.All

Response shape

{
  "ticker":           "{ticker}",
  "model_date":       "YYYY-MM-DD",
  "model_version":    "{model version}",

  "inputs":           { "{axis_key}": 0.0, "discount_rate": 0.08 },
  "defaulted_inputs": null,          // axis keys filled from the model's own deck
                                     // because the caller did not name them
  "axes": [                          // multi-axis grids only
    { "axis":    "{axis_key}",
      "accepts": ["{param}", "..."], // the parameter names this axis takes
      "bounds":  [0.0, 0.0],         // valid input range
      "nodes":   0 }
  ],

  "nav_per_share":       0.00,
  "nav_per_share_{ccy}": 0.00,       // same value, currency-suffixed key
  "currency":            "{USD | CAD | AUD}",

  "out_of_grid":        false,
  "out_of_grid_detail": null,        // per axis: "below" | "above" | false
  "grid_bounds":        { "{axis_key}": [0.0, 0.0], "discount_rate": [0.0, 0.0] },
  "interpolation":      "{n}-axis multilinear on {m} re-run scenarios",

  "disclaimer":         "..."        // extended when a value was clamped
}

nav_per_share is always accompanied by an explicit currency field (CAD, USD, or AUD depending on the company's model denomination). Never infer currency from the commodity type. Read the NAV currency from /catalog or from the currency field of the /nav-query response itself; do not infer it from the commodity or the exchange.

Example calls

# Single commodity axis — name the axis parameter and the rate.
GET /nav-query/{ticker}?gold=4000&rate=0.08

# Multiple commodity axes — name each one. ?row= is refused here.
GET /nav-query/{ticker}?gold=4000&cu=6.5&rate=0.08

# Do not know the axis? Omit it. The response reports what it defaulted.
GET /nav-query/{ticker}?rate=0.08
→ inputs echoes the axis and its value; defaulted_inputs names it

# Single-axis fallback when the parameter name is unknown.
GET /nav-query/{ticker}?row=4000&rate=0.08

Substitute {ticker} with a key from /nav-debug, and read that ticker's valid input range from its matrix_rows and matrix_cols before calling. A value outside the grid is clamped to the boundary rather than extrapolated, and the response reports that per axis in out_of_grid_detail — see “Out-of-grid inputs are clamped” below.

Interpolation behaviour

Interpolation is multilinear over however many axes that ticker's grid carries — 2n corners, each weighted by the product of its per-axis fractions. It reduces to the bilinear four-point case only when n = 2. A three-axis ticker interpolates across eight corners. The 200 response states which was used in its interpolation field, e.g. "3-axis multilinear on 150 re-run scenarios". Every node is a full model re-run, not a curve fitted through sampled points.

Out-of-grid inputs are clamped — and the response says so

An input beyond the published grid lands exactly on the edge value rather than extrapolating past it. That clamp is reported explicitly, because a clamped answer is otherwise indistinguishable from an interpolated one: same 200, same shape. Three fields carry it, and an agent should test out_of_grid before treating nav_per_share as an interpolation:

FieldMeaning
out_of_gridBoolean. True if any supplied input fell outside its axis.
out_of_grid_detailPer axis, "below", "above" or false, so you can tell which input was out of range and in which direction. null when nothing was clamped.
grid_boundsThe [min, max] actually available per axis, including discount_rate. Read this to correct a clamped call.

When a clamp occurs the disclaimer field is also extended to say the value is a boundary estimate rather than an interpolation, so a consumer that renders only the disclaimer still surfaces it.

The response describes its own axes

A multi-axis 200 response carries an axes array — for each axis, its axis key, the parameter names it accepts, its bounds and its node count. One call therefore tells an agent everything it needs to make the next call correctly, including which parameters that ticker takes. Combined with defaulted_inputs, a bare ?rate=0.08 is a valid discovery call: it returns a NAV at the model's own deck and describes the surface it came from.

Error responses

HTTPBodyMeaning
404{"error":"ticker_not_found"}Ticker not in catalog, or its JSON is not yet uploaded.
404{"error":"no_sensitivity_matrix"}A report exists but the NAV Query API is not enabled for this ticker. /nav-debug reports this in advance as has_sensitivity: false.
400{"error":"wrong_axis_param"}The parameter names no axis on this ticker — e.g. ?gold= on a silver-priced ticker. Self-correcting: the body carries axes with the parameters each axis accepts, plus expected_param. Previously such a call was interpolated against whatever axis existed and returned a plausible, wrong number.
400{"error":"ambiguous_axis_param"}Two parameters claim the same axis, or ?row= was sent to a ticker with more than one commodity axis. The body lists the axes and the parameter to send for each.
400{"error":"missing_params"}No commodity parameter and no discount rate. The body carries the full accepted parameter list and worked examples.
500{"error":"malformed_grid"}The stored grid failed validation. Not caller-correctable; report it.

Every 400 body is designed to be acted on without re-reading this page: it names the axes, the accepted parameters and, where applicable, the exact parameter to substitute.

Qatom MCP — For Autonomous Agents

AI agents that have been granted access to the Qatom MCP marketplace can discover and call Harcourt tools autonomously, with payment settled automatically from the agent's Qatom wallet. No human approval is required per transaction.

NAV Query tool

FieldValue
Tool nameharcourt_nav_query_api
Tool ID77
Price$1 USD TDN per call
MCP serverhttps://mcp.m.todaq.net/mcp
DiscoveryAvailable in Qatom MCP marketplace — search by tool name or by intent

Tool input schema

{
  "ticker":           "ivn",          // lowercase: aris | ivn | itr | vgc | dyl | sa | hl | bnkr | lug | vzla
  "commodity_param":  "cu",           // gold | cu | u3o8 | zn | row
  "commodity_price":  6.5,            // USD/lb for copper; USD/oz for gold; USD/lb for U3O8
  "discount_rate":    0.08            // decimal (0.08 = 8%)
}

PDF report tools (one per covered company)

Every covered company has its own Qatom tool returning the full NAV PDF report at $15 USD TDN. Rather than enumerate them here — a list that goes stale the moment a company is added — discover them in the Qatom marketplace by provider Harcourt Valuations, or read the authoritative list from the catalog endpoint, where each entry's report_id is also usable directly:

GET https://reports.harcourtvaluations.ai/catalog
Accept: application/json

POST https://reports.harcourtvaluations.ai/
Content-Type: application/json

{ "report_id": "<report_id from the catalog>" }

Agent wallet setup

To call Qatom-gated tools autonomously, an agent needs:

  1. A Qatom primary wallet (human-controlled, MFA-gated) — created at todaq.net
  2. An agent sandbox wallet (no MFA, funded from the primary wallet via transfer_to_agent_wallet)
  3. Sufficient TDN balance to cover the calls ($1 per NAV query, $15 per PDF report)

The agent sandbox wallet is debited automatically when a tool call succeeds. Revenue is credited to Harcourt's provider Twin in real time.

Free Chat API (Klondike Mike)

A free conversational interface to Harcourt's data. Returns plain-language NAV analysis for any covered ticker. No payment required. Useful for quick screening before purchasing a full report or calling the paid API.

POST https://reports.harcourtvaluations.ai/chat
// Request
{
  "messages": [
    { "role": "user", "content": "What's the current P/NAV on Ivanhoe Mines?" }
  ]
}

// Response (Anthropic messages format)
{
  "content": [
    {
      "type": "text",
      "text": "...prose citing that company's current NAV, P/NAV and price deck..."
    }
  ]
}

The chat agent (Klondike Mike) is built on Claude Haiku. The system prompt is generated server-side from live {ticker}.json files in R2, so it always reflects the most recent published NAV data. Conversation history should be included in the messages array for multi-turn sessions (up to 10 turns retained).

Methodology Notes for AI Consumers

When interpreting Harcourt NAV figures, AI agents and models should be aware of:

Disclaimer: Harcourt Valuations reports and API responses are analytical tools for informational purposes only. They do not constitute investment advice, a formal appraisal, or a recommendation to buy or sell any security. All valuations are model estimates subject to uncertainty. AI agents and models using this data must not present it as investment advice to end users without appropriate disclaimers.