# One run, and every receipt behind it

A number is worth what its working is worth. This page takes **one real price
run** — made on 2026-08-12 against the environment this build deploys to — and
puts the whole of it in public: the run id, the complete assumption set, the
data vintage the number was computed from, and a block you can run that
reproduces that recorded receipt **byte for byte**.

Nothing here is a screenshot and nothing here is retyped. The block below is
extracted from this page verbatim and executed against a live environment on
every build, so a page that has drifted from the product is a red build rather
than a stale claim.

Read [the quickstart](quickstart.md) for `CEDE_BASE_URL`, the
`Authorization: Bearer` header and the job pattern, and
[A technical price, from nothing](price.md) for what the Price verb does. This
page assumes both and is about one thing only: whether the number can be
checked by somebody who does not trust us.

---

## What you need

* `curl` 7.76 or newer (this page uses `--fail-with-body`) and `python3`.
* `CEDE_BASE_URL` — the base URL of the Craton environment you are pointed at,
  with no trailing slash.

```sh
export CEDE_BASE_URL="https://altier.ridgehead-hamlet.ts.net:8472"   # no trailing slash
```

If you already hold a key, export `CEDE_API_KEY` too; the block signs up for
its own account when you do not.

---

## The recorded run

| | |
| --- | --- |
| run id | `price-v0:109ea9c675e556f5` |
| object it priced | `21614af3-a8a6-4d8a-a0b1-224ac4da3ba9` |
| receipt digest | `sha256:e6e54f0de0c468a95f9c258d2514c9a9e298dc3d2c626358daa733350b23895e` |
| model | `cede/parametric-burn-eq-box` 0.1.0 |
| technical price | **449,191,636 JPY**, rate on line 0.179677 |
| expected loss | 275,000,000 JPY |
| burn | 0.11 — 10 of 40 years on the record would have paid |
| structure | Tokyo Bay cat-in-a-box, 2,500,000,000 JPY limit, attaching at M5.5, exhausting at M6.5, focal depth 100 km or shallower |
| cover period | 2026-07-01 to 2027-07-01, Asia/Tokyo, 365 days |

The **receipt digest** is the sha256 of the priced result as the API rendered
it: sorted keys, no insignificant whitespace, UTF-8, numbers written from their
own digits. Every byte that digest covers is printed below.

### The data vintage the number came from

One feed, pinned, hashed, and read from disk — the engine makes no network call
while it prices, which is the whole reason a run replays at all.

| | |
| --- | --- |
| feed | `usgs-eq-kanto` |
| publisher | U.S. Geological Survey (ANSS ComCat, FDSN event web service) |
| version / vintage | `2026-08-10` / `2026-08-10` |
| snapshot sha256 | `ec7c4b0c6b97def7aec99dfd96446c00a980d5af8bd81bf6edc086f44d4f68e1` |
| records | 2409 events |
| licence | public domain |
| period of record | 1986-01-01 to 2026-01-01, 40 years |
| events inside the box | 329, of which 13 qualifying occurrences |

The snapshot's own sha256 is recomputed before a single event is read, and a
mismatch is an error rather than a silent re-fetch. It is also one of the four
inputs the run id is hashed from, which is why a re-run against a different
vintage cannot quietly wear the same id.

---

## The complete assumption set

This is the recorded run, entire — the document `sha256:12ac550e…` is taken
over, pretty-printed here and canonical there. Forty annual payout ratios, the
thirteen qualifying occurrences by the publisher's own event id, every loading
with its rate and its rationale, and the six limitations the model states about
itself.

```json
{
  "assumptions": {
    "burn": {
      "annual_payout_ratios": [
        {
          "payout_ratio": 0,
          "year": 1986
        },
        {
          "payout_ratio": 1,
          "year": 1987
        },
        {
          "payout_ratio": 0,
          "year": 1988
        },
        {
          "payout_ratio": 0.3,
          "year": 1989
        },
        {
          "payout_ratio": 0.65,
          "year": 1990
        },
        {
          "payout_ratio": 0,
          "year": 1991
        },
        {
          "payout_ratio": 0.3,
          "year": 1992
        },
        {
          "payout_ratio": 0,
          "year": 1993
        },
        {
          "payout_ratio": 0,
          "year": 1994
        },
        {
          "payout_ratio": 0,
          "year": 1995
        },
        {
          "payout_ratio": 0,
          "year": 1996
        },
        {
          "payout_ratio": 0,
          "year": 1997
        },
        {
          "payout_ratio": 0,
          "year": 1998
        },
        {
          "payout_ratio": 0,
          "year": 1999
        },
        {
          "payout_ratio": 0,
          "year": 2000
        },
        {
          "payout_ratio": 0,
          "year": 2001
        },
        {
          "payout_ratio": 0,
          "year": 2002
        },
        {
          "payout_ratio": 0,
          "year": 2003
        },
        {
          "payout_ratio": 0.3,
          "year": 2004
        },
        {
          "payout_ratio": 0.3,
          "year": 2005
        },
        {
          "payout_ratio": 0,
          "year": 2006
        },
        {
          "payout_ratio": 0,
          "year": 2007
        },
        {
          "payout_ratio": 0,
          "year": 2008
        },
        {
          "payout_ratio": 0,
          "year": 2009
        },
        {
          "payout_ratio": 0,
          "year": 2010
        },
        {
          "payout_ratio": 0.65,
          "year": 2011
        },
        {
          "payout_ratio": 0.3,
          "year": 2012
        },
        {
          "payout_ratio": 0.3,
          "year": 2013
        },
        {
          "payout_ratio": 0,
          "year": 2014
        },
        {
          "payout_ratio": 0,
          "year": 2015
        },
        {
          "payout_ratio": 0,
          "year": 2016
        },
        {
          "payout_ratio": 0,
          "year": 2017
        },
        {
          "payout_ratio": 0,
          "year": 2018
        },
        {
          "payout_ratio": 0,
          "year": 2019
        },
        {
          "payout_ratio": 0,
          "year": 2020
        },
        {
          "payout_ratio": 0.3,
          "year": 2021
        },
        {
          "payout_ratio": 0,
          "year": 2022
        },
        {
          "payout_ratio": 0,
          "year": 2023
        },
        {
          "payout_ratio": 0,
          "year": 2024
        },
        {
          "payout_ratio": 0,
          "year": 2025
        }
      ],
      "burn_rate": 0.11,
      "standard_deviation": 0.223103115173,
      "triggering_years": 10,
      "years": 40
    },
    "cover_period": {
      "annualisation_basis": "elapsed days divided by 365; the annual burn is multiplied by the result",
      "days": 365,
      "expiry": "2027-07-01T00:00:00+09:00",
      "factor": 1,
      "inception": "2026-07-01T00:00:00+09:00",
      "timezone": "Asia/Tokyo"
    },
    "currency_basis": {
      "currency": "JPY",
      "fx_conversion_applied": false,
      "minor_units": 0,
      "rounding": "ROUND_HALF_UP to the currency's minor unit"
    },
    "data_sources": [
      {
        "id": "usgs-eq-kanto",
        "kind": "quake_catalogue",
        "licence": "public domain",
        "name": "USGS ANSS Comprehensive Earthquake Catalog — Kanto extract",
        "publisher": "U.S. Geological Survey (ANSS ComCat, FDSN event web service)",
        "query_url": "https://earthquake.usgs.gov/fdsnws/event/1/query?format=csv&starttime=1986-01-01T00:00:00&endtime=2026-01-01T00:00:00&minlatitude=34.0&maxlatitude=37.0&minlongitude=138.0&maxlongitude=141.5&minmagnitude=4.5&orderby=time-asc&eventtype=earthquake",
        "record_count": 2409,
        "sha256": "ec7c4b0c6b97def7aec99dfd96446c00a980d5af8bd81bf6edc086f44d4f68e1",
        "version": "2026-08-10",
        "vintage": "2026-08-10"
      }
    ],
    "event_selection": {
      "box": {
        "bounds": "inclusive on every edge; an event exactly on an edge is inside the box",
        "max_latitude": 36,
        "max_longitude": 140.4,
        "min_latitude": 35,
        "min_longitude": 139.2
      },
      "events_in_box": 329,
      "events_in_snapshot": 2409,
      "magnitude_basis": "catalogue preferred magnitude as published (mixed magType: mb, mw, mwb, mwc, mwr, mww)",
      "max_focal_depth_km": 100,
      "minimum_magnitude": 5.5,
      "occurrences": [
        {
          "clustered_event_count": 1,
          "depth_km": 62.9,
          "event_id": "usp0003bbq",
          "latitude": 35.362,
          "longitude": 140.214,
          "magnitude": 6.7,
          "magnitude_type": "mw",
          "payout_ratio": 1,
          "time": "1987-12-17T02:08:19.920000Z"
        },
        {
          "clustered_event_count": 1,
          "depth_km": 60.4,
          "event_id": "usp0003s2r",
          "latitude": 35.964,
          "longitude": 139.788,
          "magnitude": 5.6,
          "magnitude_type": "mw",
          "payout_ratio": 0.3,
          "time": "1989-02-19T12:27:09.950000Z"
        },
        {
          "clustered_event_count": 1,
          "depth_km": 71.2,
          "event_id": "usp0003udm",
          "latitude": 35.842,
          "longitude": 140.361,
          "magnitude": 5.5,
          "magnitude_type": "mb",
          "payout_ratio": 0.3,
          "time": "1989-04-25T17:18:38.040000Z"
        },
        {
          "clustered_event_count": 1,
          "depth_km": 67,
          "event_id": "usp0004a05",
          "latitude": 35.522,
          "longitude": 140.339,
          "magnitude": 6.3,
          "magnitude_type": "mw",
          "payout_ratio": 0.65,
          "time": "1990-06-01T01:22:11.590000Z"
        },
        {
          "clustered_event_count": 1,
          "depth_km": 100,
          "event_id": "usp00052gf",
          "latitude": 35.106,
          "longitude": 139.644,
          "magnitude": 5.8,
          "magnitude_type": "mw",
          "payout_ratio": 0.3,
          "time": "1992-02-01T19:04:04.530000Z"
        },
        {
          "clustered_event_count": 1,
          "depth_km": 64,
          "event_id": "usp000d5w4",
          "latitude": 35.95,
          "longitude": 139.919,
          "magnitude": 5.8,
          "magnitude_type": "mwc",
          "payout_ratio": 0.3,
          "time": "2004-10-06T14:40:39.920000Z"
        },
        {
          "clustered_event_count": 1,
          "depth_km": 46.2,
          "event_id": "usp000dg7q",
          "latitude": 35.982,
          "longitude": 139.686,
          "magnitude": 5.5,
          "magnitude_type": "mwb",
          "payout_ratio": 0.3,
          "time": "2005-02-15T19:46:35.880000Z"
        },
        {
          "clustered_event_count": 1,
          "depth_km": 61,
          "event_id": "usp000dvqt",
          "latitude": 35.498,
          "longitude": 139.982,
          "magnitude": 5.9,
          "magnitude_type": "mwb",
          "payout_ratio": 0.3,
          "time": "2005-07-23T07:34:56.770000Z"
        },
        {
          "clustered_event_count": 1,
          "depth_km": 43,
          "event_id": "usp000j0c3",
          "latitude": 35.579,
          "longitude": 140.305,
          "magnitude": 6.2,
          "magnitude_type": "mww",
          "payout_ratio": 0.65,
          "time": "2011-04-21T13:37:03.340000Z"
        },
        {
          "clustered_event_count": 1,
          "depth_km": 44,
          "event_id": "usp000jjs6",
          "latitude": 35.596,
          "longitude": 140.349,
          "magnitude": 5.8,
          "magnitude_type": "mww",
          "payout_ratio": 0.3,
          "time": "2012-04-29T10:28:51.870000Z"
        },
        {
          "clustered_event_count": 1,
          "depth_km": 64.31,
          "event_id": "usb000kvca",
          "latitude": 35.9187,
          "longitude": 139.9684,
          "magnitude": 5.6,
          "magnitude_type": "mww",
          "payout_ratio": 0.3,
          "time": "2013-11-09T22:37:50.410000Z"
        },
        {
          "clustered_event_count": 1,
          "depth_km": 59.4,
          "event_id": "usb000kzuj",
          "latitude": 35.6039,
          "longitude": 140.1529,
          "magnitude": 5.5,
          "magnitude_type": "mwb",
          "payout_ratio": 0.3,
          "time": "2013-11-16T11:44:41.340000Z"
        },
        {
          "clustered_event_count": 1,
          "depth_km": 62,
          "event_id": "us6000fsl6",
          "latitude": 35.5736,
          "longitude": 140.0705,
          "magnitude": 5.9,
          "magnitude_type": "mww",
          "payout_ratio": 0.3,
          "time": "2021-10-07T13:41:24.528000Z"
        }
      ],
      "qualifying_occurrences": 13
    },
    "frequency": [
      {
        "annual_frequency": 0.325,
        "label": "attachment",
        "level": 5.5,
        "occurrences": 13,
        "payout_ratio": 0.3,
        "return_period_years": 3.076923076923,
        "unit": "M"
      },
      {
        "annual_frequency": 0.075,
        "level": 6,
        "occurrences": 3,
        "payout_ratio": 0.65,
        "return_period_years": 13.333333333333,
        "unit": "M"
      },
      {
        "annual_frequency": 0.025,
        "label": "exhaustion",
        "level": 6.5,
        "occurrences": 1,
        "payout_ratio": 1,
        "return_period_years": 40,
        "unit": "M"
      }
    ],
    "limitations": [
      "Magnitudes are the catalogue's preferred magnitude and mix magnitude types across the record. No conversion to a common magnitude scale is applied, so a threshold is read against whichever scale the publisher preferred for that solution.",
      "The catalogue is revised after the fact. This price reads one pinned vintage of it; a later vintage can move a magnitude across a threshold and change the burn.",
      "Forty years of one box is a small sample. The parameter uncertainty loading is a flat allowance for that, not a fitted confidence interval.",
      "Occurrence basis: a year's payout ratio is its largest single occurrence. A second occurrence in the same year adds nothing to the burn, and reinstatements are not priced.",
      "Occurrence is taken as uniform in time — no seasonality adjustment. That is appropriate for earthquake and would not be for a wind season.",
      "This is a price for the index, not for the loss. A parametric structure pays on the measurement; the difference between that and the insured's actual loss is basis risk, and v0 does not quantify it."
    ],
    "loadings": [
      {
        "amount": {
          "amount": 41250000,
          "currency": "JPY"
        },
        "applies_to": "expected_loss",
        "arithmetic": "0.15 x 275000000 JPY = 41250000 JPY",
        "base": {
          "amount": 275000000,
          "arithmetic": "275000000 (expected_loss) = 275000000 JPY",
          "currency": "JPY",
          "operation": "sum",
          "terms": [
            {
              "currency": "JPY",
              "name": "expected_loss",
              "value": 275000000
            }
          ]
        },
        "basis": "multiplicative on the expected loss",
        "name": "parameter_uncertainty",
        "rate": 0.15,
        "rationale": "The burn rate is an average over a small number of observed occurrences, so it is itself uncertain. A flat allowance on the expected loss, not a fitted confidence interval."
      },
      {
        "amount": {
          "amount": 111551558,
          "currency": "JPY"
        },
        "applies_to": "standard_deviation x limit x cover period factor",
        "arithmetic": "0.2 x 557757788 JPY = 111551558 JPY",
        "base": {
          "amount": 557757788,
          "arithmetic": "0.223103115173 (standard_deviation) x 2500000000 (limit) x 1 (cover_period_factor) = 557757788 JPY",
          "currency": "JPY",
          "operation": "product",
          "terms": [
            {
              "name": "standard_deviation",
              "value": 0.223103115173
            },
            {
              "currency": "JPY",
              "name": "limit",
              "value": 2500000000
            },
            {
              "name": "cover_period_factor",
              "value": 1
            }
          ]
        },
        "basis": "multiplicative on the standard deviation of the annual payout ratio",
        "name": "volatility",
        "rate": 0.2,
        "rationale": "A structure that pays nothing in most years and a quarter of the limit in a few is more expensive to carry than its mean suggests. This charges the year-to-year spread of the burn."
      },
      {
        "amount": {
          "amount": 21390078,
          "currency": "JPY"
        },
        "applies_to": "expected_loss + parameter_uncertainty + volatility",
        "arithmetic": "0.05 x 427801558 JPY = 21390078 JPY",
        "base": {
          "amount": 427801558,
          "arithmetic": "275000000 (expected_loss) + 41250000 (parameter_uncertainty) + 111551558 (volatility) = 427801558 JPY",
          "currency": "JPY",
          "operation": "sum",
          "terms": [
            {
              "currency": "JPY",
              "name": "expected_loss",
              "value": 275000000
            },
            {
              "currency": "JPY",
              "name": "parameter_uncertainty",
              "value": 41250000
            },
            {
              "currency": "JPY",
              "name": "volatility",
              "value": 111551558
            }
          ]
        },
        "basis": "multiplicative on the expected loss and the loadings above",
        "name": "expense",
        "rate": 0.05,
        "rationale": "Allowance for the cost of running the structure — index calculation, monitoring and reporting over the cover period."
      }
    ],
    "method": {
      "clustering": {
        "rule": "qualifying events within the index's aggregation window of the first event of a cluster are one occurrence; the window is measured from that first event, never chained from the previous one",
        "window": "PT72H"
      },
      "description": "For each year of the period of record, the largest qualifying occurrence in the box sets that year's payout ratio through the index's step function. The burn rate is the mean of those ratios over every year, loss years and zero years alike; the expected loss is the burn rate times the limit times the cover period factor. No frequency-magnitude relation is fitted and no distribution is assumed: this is the history, counted.",
      "family": "burn",
      "name": "empirical_annual_burning_cost",
      "occurrence_basis": "annual maximum occurrence payout ratio, years attributed by the UTC year of the occurrence's first event",
      "ratio_precision": "1E-12",
      "reinstatements_considered": false
    },
    "period_of_record": {
      "end": "2026-01-01T00:00:00Z",
      "start": "1986-01-01T00:00:00Z",
      "years": 40
    },
    "price_build_up": {
      "currency": "JPY",
      "expected_loss": {
        "amount": {
          "amount": 275000000,
          "currency": "JPY"
        },
        "base": {
          "amount": 275000000,
          "arithmetic": "0.11 (burn_rate) x 2500000000 (limit) x 1 (cover_period_factor) = 275000000 JPY",
          "currency": "JPY",
          "operation": "product",
          "terms": [
            {
              "name": "burn_rate",
              "value": 0.11
            },
            {
              "currency": "JPY",
              "name": "limit",
              "value": 2500000000
            },
            {
              "name": "cover_period_factor",
              "value": 1
            }
          ]
        }
      },
      "statement": "expected_loss + parameter_uncertainty + volatility + expense = the technical price. Every term is rounded to the currency's minor unit before the next one is computed from it, so each loading is exactly its rate times its published base and the four components add to the published premium with nothing left over.",
      "steps": [
        {
          "amount": {
            "amount": 275000000,
            "currency": "JPY"
          },
          "component": "expected_loss",
          "running_total": {
            "amount": 275000000,
            "currency": "JPY"
          }
        },
        {
          "amount": {
            "amount": 41250000,
            "currency": "JPY"
          },
          "component": "parameter_uncertainty",
          "running_total": {
            "amount": 316250000,
            "currency": "JPY"
          }
        },
        {
          "amount": {
            "amount": 111551558,
            "currency": "JPY"
          },
          "component": "volatility",
          "running_total": {
            "amount": 427801558,
            "currency": "JPY"
          }
        },
        {
          "amount": {
            "amount": 21390078,
            "currency": "JPY"
          },
          "component": "expense",
          "running_total": {
            "amount": 449191636,
            "currency": "JPY"
          }
        }
      ],
      "technical_price": {
        "amount": 449191636,
        "currency": "JPY"
      }
    }
  },
  "expected_loss": {
    "amount": 275000000,
    "currency": "JPY"
  },
  "model": {
    "name": "cede/parametric-burn-eq-box",
    "version": "0.1.0"
  },
  "run_id": "price-v0:109ea9c675e556f5",
  "schema_version": "0.1.0",
  "subject": {
    "index": {
      "name": "tokyo-bay-eq-box-magnitude",
      "version": "1.0.0"
    },
    "object_id": "21614af3-a8a6-4d8a-a0b1-224ac4da3ba9",
    "peril": "earthquake",
    "schema_version": "0.1.0",
    "trigger_type": "parametric_cat_in_a_box"
  },
  "technical_price": {
    "amount": 449191636,
    "assumptions_ref": "price-v0:109ea9c675e556f5",
    "basis": "technical",
    "currency": "JPY",
    "disclosure": "Technical price: an actuarial estimate produced by analytics software from the assumptions stated in this response. It is not a price at which any party stands ready to transact, and it carries no capacity, no acceptance and no validity period.",
    "rate_on_line": 0.179677
  },
  "verb": "price"
}
```

---

## Reproduce it

Run this in an empty directory. It prices the same structure on the environment
you point it at, then proves three things about what comes back:

1. **The run id is honest arithmetic.** It recomputes your run's content
   address from public inputs — the model and its version, the response schema
   version, the feed snapshot's digest, and the stored object — and checks the
   server's answer against it.
2. **The recorded run id recomputes too.** The same arithmetic, over the object
   id at the top of this page, must land on `price-v0:109ea9c675e556f5`.
3. **The recorded receipt reproduces byte for byte.** Your result, carrying the
   recorded object's identity, canonicalises to bytes whose sha256 is
   `12ac550e…` — the digest printed above, the digest of a run made on a
   different day, by a different account, on a different object.

Then it prices the same object a second time and shows you exactly what moves
and what does not.

<!-- cede:runnable -->

```bash
set -euo pipefail

: "${CEDE_BASE_URL:?export CEDE_BASE_URL first — see 'What you need'}"

# The recorded run this page walks. Every one of these three values is printed
# in the tables above, and every one is checked against a live run below.
recorded_run_id="price-v0:109ea9c675e556f5"
recorded_object_id="21614af3-a8a6-4d8a-a0b1-224ac4da3ba9"
recorded_receipt_sha256="e6e54f0de0c468a95f9c258d2514c9a9e298dc3d2c626358daa733350b23895e"

# Read one field out of a JSON document on stdin: `field a.b.0.c`.
field() {
  python3 -c 'import json, sys
document = json.load(sys.stdin)
for step in sys.argv[1].split("."):
    document = document[int(step)] if step.isdigit() else document[step]
print(document)' "$1"
}

# Craton renders every response through one canonical serialisation, and a digest
# is only as meaningful as the rule the bytes were written by — so the rule is
# restated here in full rather than asked for on trust: object keys sorted by
# code point, no insignificant whitespace, UTF-8 text, and numbers written from
# their own digits instead of through a binary float.
cat > canonical.py <<'PY'
import json
from decimal import Decimal


def canonical(value):
    if value is None:
        return "null"
    if value is True:
        return "true"
    if value is False:
        return "false"
    if isinstance(value, Decimal):
        if value == 0:
            return "0"
        text = format(value.normalize(), "f")
        return text.rstrip("0").rstrip(".") if "." in text else text
    if isinstance(value, str):
        return json.dumps(value, ensure_ascii=False)
    if isinstance(value, list):
        return "[" + ",".join(canonical(item) for item in value) + "]"
    return (
        "{"
        + ",".join(
            json.dumps(key, ensure_ascii=False) + ":" + canonical(value[key])
            for key in sorted(value)
        )
        + "}"
    )


def load(path):
    """A JSON document with every number kept as its own digits."""
    with open(path, encoding="utf-8") as handle:
        return json.load(handle, parse_float=Decimal, parse_int=Decimal)
PY

# 1 — An account, if you did not bring one.
if [ -z "${CEDE_API_KEY:-}" ]; then
  CEDE_API_KEY=$(curl -sS --fail-with-body \
    -H "Content-Type: application/json" \
    -d '{"label": "receipts walkthrough"}' \
    "$CEDE_BASE_URL/signup" | field api_key.secret)
  export CEDE_API_KEY
  printf 'your key (shown once): %s\n' "$CEDE_API_KEY"
fi
auth="Authorization: Bearer $CEDE_API_KEY"

# 2 — The structure the recorded run priced, to the byte. A Tokyo Bay
#     cat-in-a-box on a single warehouse: pays a third of the limit at M5.5,
#     two thirds at M6.0, all of it at M6.5, for shocks no deeper than 100 km.
cat > structure.json <<'JSON'
{
  "schema_version": "0.1.0",
  "status": "analysed",
  "exposure": {
    "kind": "location_schedule",
    "currency": "JPY",
    "locations": [
      { "ref": "YOK-001",
        "address_as_given": "1-1 Daikoku-futo, Tsurumi-ku, Yokohama 230-0054",
        "latitude": 35.4693, "longitude": 139.6852,
        "geocode": { "resolution": "rooftop", "confidence": 0.94 },
        "occupancy": "warehouse", "construction": "steel",
        "year_built": 2004,
        "values": { "building": { "amount": 5200000000, "currency": "JPY" } } }
    ],
    "source_fidelity": { "unmapped_columns": [], "guessed_units": [], "ambiguous_rows": [] }
  },
  "peril": {
    "code": "earthquake",
    "region": {
      "description": "Tokyo Bay cat-in-a-box: 35.0-36.0N, 139.2-140.4E.",
      "bounding_geometry": {
        "type": "Polygon",
        "coordinates": [[[139.2, 35.0], [140.4, 35.0], [140.4, 36.0],
                         [139.2, 36.0], [139.2, 35.0]]]
      }
    }
  },
  "financial_structure": {
    "limit": { "amount": 2500000000, "currency": "JPY" },
    "attachment": { "value": 5.5, "unit": "M", "index_ref": "tokyo-bay-eq-box-magnitude" }
  },
  "trigger": {
    "type": "parametric_cat_in_a_box",
    "index": {
      "name": "tokyo-bay-eq-box-magnitude",
      "version": "1.0.0",
      "description": "Largest catalogue magnitude inside the Tokyo Bay box, shallower than the depth threshold.",
      "measurement": { "variable": "catalogue_preferred_magnitude", "unit": "M", "statistic": "max" },
      "aggregation_window": { "duration": "PT72H", "alignment": "event" },
      "thresholds": [
        { "label": "attachment", "level": 5.5, "unit": "M" },
        { "label": "exhaustion", "level": 6.5, "unit": "M" },
        { "label": "max_focal_depth", "level": 100, "unit": "km" }
      ],
      "payout_function": {
        "type": "step",
        "points": [ { "level": 5.5, "payout_ratio": 0.3 },
                    { "level": 6.0, "payout_ratio": 0.65 },
                    { "level": 6.5, "payout_ratio": 1 } ],
        "maximum_payout_ratio": 1
      }
    },
    "data_sources": [
      { "id": "usgs-eq-kanto",
        "name": "USGS ANSS Comprehensive Earthquake Catalog - Kanto extract",
        "kind": "quake_catalogue",
        "version": "2026-08-10", "vintage": "2026-08-10" }
    ]
  },
  "period": {
    "inception": "2026-07-01T00:00:00+09:00",
    "expiry": "2027-07-01T00:00:00+09:00",
    "timezone": "Asia/Tokyo"
  }
}
JSON

object_id=$(curl -sS --fail-with-body -H "$auth" \
  -H "Content-Type: application/json" \
  --data-binary @structure.json \
  "$CEDE_BASE_URL/objects" | field id)
printf 'your object: %s\n' "$object_id"

# 3 — Price it, and wait for the job to stop moving.
job_id=$(curl -sS --fail-with-body -X POST -H "$auth" \
  "$CEDE_BASE_URL/objects/$object_id/price" | field id)
state=unknown
for _ in $(seq 1 300); do
  curl -sS --fail-with-body -H "$auth" "$CEDE_BASE_URL/jobs/$job_id" > job.json
  state=$(field status < job.json)
  case "$state" in succeeded|failed) break ;; esac
  sleep 0.2
done
if [ "$state" != succeeded ]; then
  printf 'the price job did not succeed: %s\n' "$(cat job.json)" >&2
  exit 1
fi

# 4 — The receipt, and the three checks on it.
python3 - "$recorded_run_id" "$recorded_object_id" "$recorded_receipt_sha256" <<'PY'
import copy
import hashlib
import sys

from canonical import canonical, load

recorded_run_id, recorded_object_id, recorded_digest = sys.argv[1:4]

receipt = load("job.json")["result"]["price"]
posted = load("structure.json")
source = receipt["assumptions"]["data_sources"][0]
burn = receipt["assumptions"]["burn"]
record = receipt["assumptions"]["period_of_record"]

print("technical price:", receipt["technical_price"]["amount"],
      receipt["technical_price"]["currency"],
      "- rate on line", receipt["technical_price"]["rate_on_line"])
print("expected loss:  ", receipt["expected_loss"]["amount"],
      receipt["expected_loss"]["currency"])
print("burn rate:", burn["burn_rate"], "-", burn["triggering_years"], "of",
      burn["years"], "years would have paid, over", record["start"][:4], "-",
      record["end"][:4])
for loading in receipt["assumptions"]["loadings"]:
    print("  loading:", loading["name"], "at", loading["rate"], "=",
          loading["amount"]["amount"], loading["amount"]["currency"])
print("data vintage:", source["id"], source["version"], "sha256",
      source["sha256"], "-", source["licence"])
print("limitations stated on the result:",
      len(receipt["assumptions"]["limitations"]))


def run_id(object_id):
    """A price run's content address, recomputed from public inputs alone."""
    material = {
        "model": receipt["model"]["name"],
        "model_version": receipt["model"]["version"],
        "response_schema_version": receipt["schema_version"],
        "feed": {
            "id": source["id"],
            "version": source["version"],
            "sha256": source["sha256"],
        },
        "object": dict(posted, id=object_id),
    }
    digest = hashlib.sha256(canonical(material).encode("utf-8")).hexdigest()
    return "price-v0:" + digest[:16]


def check(name, got, want):
    if got != want:
        raise SystemExit(f"FAIL {name}\n  got  {got}\n  want {want}")
    print(f"ok  {name}: {got}")


# (1) the run id the server returned is the digest of the run's own inputs.
check("your run id is the content address of your inputs",
      run_id(receipt["subject"]["object_id"]), receipt["run_id"])

# (2) the same arithmetic over the recorded object lands on the recorded id.
rebuilt_run_id = run_id(recorded_object_id)
check("the recorded run id recomputes", rebuilt_run_id, recorded_run_id)

# (3) your result, wearing the recorded run's identity, is the recorded
#     receipt — the same bytes, and so the same digest.
rebuilt = copy.deepcopy(receipt)
rebuilt["subject"]["object_id"] = recorded_object_id
rebuilt["run_id"] = rebuilt_run_id
rebuilt["technical_price"]["assumptions_ref"] = rebuilt_run_id
check("the recorded receipt reproduces byte for byte",
      hashlib.sha256(canonical(rebuilt).encode("utf-8")).hexdigest(),
      recorded_digest)
PY

# 5 — Price the same object again. Everything the number rests on is the same
#     bytes; the content address is not, and the next section says why.
job_id=$(curl -sS --fail-with-body -X POST -H "$auth" \
  "$CEDE_BASE_URL/objects/$object_id/price" | field id)
state=unknown
for _ in $(seq 1 300); do
  curl -sS --fail-with-body -H "$auth" "$CEDE_BASE_URL/jobs/$job_id" > job-again.json
  state=$(field status < job-again.json)
  case "$state" in succeeded|failed) break ;; esac
  sleep 0.2
done
if [ "$state" != succeeded ]; then
  printf 'the second price job did not succeed: %s\n' "$(cat job-again.json)" >&2
  exit 1
fi

python3 - <<'PY'
from canonical import canonical, load

first = load("job.json")["result"]["price"]
again = load("job-again.json")["result"]["price"]

for part in ("assumptions", "expected_loss", "subject"):
    if canonical(first[part]) != canonical(again[part]):
        raise SystemExit(f"FAIL the second run changed {part}")
print("ok  the assumption set, the expected loss and the subject are the same bytes")

priced = {key: value for key, value in first["technical_price"].items()
          if key != "assumptions_ref"}
if canonical(priced) != canonical({key: value
                                   for key, value in again["technical_price"].items()
                                   if key != "assumptions_ref"}):
    raise SystemExit("FAIL the second run changed the price")
print("ok  the technical price is the same bytes:", priced["amount"],
      priced["currency"])
print("    run id, first time: ", first["run_id"])
print("    run id, second time:", again["run_id"])
PY

echo "done — one recorded run, reproduced from its own inputs"
```

If the last line printed, every check above passed against the environment you
pointed at.

---

## Why your run id is not the recorded one

`run_id` is a content address. It is the sha256 of exactly four things — the
model, the model version, the response schema version, the feed snapshot's own
sha256 — plus the stored object, and the stored object carries the id the
server gave it when you posted it. Your object is not the recorded object, so
your run has its own address. Everything else about the two results is the same
bytes, which is what check (3) proves: give your result the recorded object's
identity and the digest is the recorded one.

The second price run is the same story told a different way. Pricing an object
**writes the price back into it**: the premium lands on the financial
structure with a pointer to the run, and the run is appended to the object's
provenance. So pricing it a second time is pricing an object that has changed —
same structure, same catalogue, same number, new content address. That is
stated here rather than smoothed over, because a reader checking digests will
find it in about four minutes and should find it from us first.

What none of this touches: the assumption set, the expected loss and the
technical price are byte-identical every time, on any environment, for any
account. That is the property the receipt is for.

---

## What a receipt is for

* **You can recompute the number.** Every loading is itemised with its rate and
  with its base — the money that rate multiplied, as a figure, beside the terms
  that figure is the product or the sum of — so `rate x base` lands on the
  published amount exactly. `assumptions.price_build_up` walks the same four
  numbers as a running total, and the itemisation adds up: expected loss plus
  the three loadings is the technical price, to the yen.
* **You can find the events.** The thirteen qualifying occurrences are listed
  by the publisher's own event id, with time, hypocentre, magnitude and the
  payout ratio each one earned.
* **You can check the data.** The snapshot's sha256 is on the result. Fetch the
  publisher's extract, hash it, compare.
* **You can see what it is weak at.** Six limitations travel on the result
  itself — mixed magnitude scales, catalogue revision, a forty-year sample, the
  occurrence basis, no seasonality, and the basis risk a parametric structure
  carries by construction. They are fields, not footnotes.

A technical price is an actuarial estimate produced by analytics software from
the assumptions stated next to it. Nobody stands ready to transact at it, it
carries no capacity and no acceptance, and Craton never moves money or touches
paper.

---

## When it does not work

| What you see | What it means |
| --- | --- |
| `FAIL the recorded receipt reproduces byte for byte` | The environment you pointed at computed a different assumption set for the same structure — a different feed vintage, or a model version that has moved. Compare the `data vintage` line the block printed against the table above. |
| `FAIL the recorded run id recomputes` | The four hashed inputs are not what this page records: read the run id, model version and snapshot digest the block printed and see which one moved. |
| `401 unauthenticated` after step 1 | The header is exactly `Authorization: Bearer <key>`. Keys live in memory and a redeploy clears them; sign up again, it takes one call. |
| Job `failed` with `422 unpriceable_object` | The structure is outside what Price v0 reads. [The technical price page](price.md) states the v0 scope; [the error reference](errors.md) is the full list of refusals. |

---

*The block on this page is extracted verbatim and executed against a live
environment on every change to Craton. If the recorded run stopped reproducing,
the build stops too.*
