Canonical Format

The v1 pipe-delimited canonical payload specification — the string that gets signed.

Envelope Format

Every attestation follows a two-layer structure: a fixed envelope wrapping type-specific payload fields. Signing and verification operate on the complete canonical string.

envelope structure
VERSION | TYPE | <payload fields...> | TIMESTAMP | NONCE

Envelope Fields

FieldPositionTypeExampleDescription
VERSION0stringv1Format version.
TYPE1stringPRICE, ECON, VOLATILITY, SENTIMENT, STRESS, GAS, GAS_INDEX, WEATHERAttestation type — determines payload structure. COMMODITIES use TYPE=ECON with REGION=COMMODITIES.
TIMESTAMPN-1integer1741514400Unix timestamp (seconds). Always second-to-last.
NONCENinteger4829106-digit random nonce. Always last field.

PRICE Type

PRICE format
v1|PRICE|PAIR|PRICE_VALUE|CURRENCY|DECIMALS|SOURCES|METHOD|TIMESTAMP|NONCE
BTC/USD example
v1|PRICE|BTCUSD|84231.50|USD|2|binance,binance_us,bitfinex,bitstamp,coinbase,gateio,gemini,kraken,okx|median|1741514400|482910
EUR/USD example
v1|PRICE|EURUSD|1.15000|USD|5|bankofcanada,bitstamp,cnb,ecb,ecbdirect,kraken,norgesbank|median|1741514400|719304
Numeric precision rules

PRICE_VALUE is always zero-padded to exactly DECIMALS decimal places (e.g. 84231.50 not 84231.5). Default decimals: JPY pairs = 0, USD/EUR/GBP/CHF/CNY/CAD = 2. Some pairs override this (e.g. EUR/USD uses 5). SOURCES are always lexicographically sorted (ASCII order). Any deviation in precision or ordering will cause signature verification to fail.

ECON Type

ECON format
v1|ECON|REGION|INDICATOR|VALUE|UNIT|PERIOD|VINTAGEDATE|SOURCEAGENCY|SERIESID|SOURCEMODEL|TIMESTAMP|NONCE
US CPI example
v1|ECON|US|CPI|326.785|index198284100|2026-02|2026-03-14|BLS|CUUR0000SA0|directapi|1741514400|830114

COMMODITIES (ECON subtype)

COMMODITIES format
v1|ECON|COMMODITIES|SYMBOL|VALUE|UNIT|PERIOD|VINTAGEDATE|SOURCEAGENCY|SERIESID|directapi|TIMESTAMP|NONCE
WTI crude example
v1|ECON|COMMODITIES|WTI|94.65|usdperbarrel|2026-03-09|2026-03-15|EIA|DCOILWTICO|directapi|1741514400|402341
COMMODITIES vs regional ECON

COMMODITIES shares the ECON TYPE but uses COMMODITIES in the REGION field, followed by SYMBOL (e.g. WTI, BRENT, NATGAS) in the INDICATOR position. The SOURCEMODEL is always directapi. Regional ECON uses a country code (US, EU) in the REGION field and a variable SOURCEMODEL.

VOLATILITY Type (MSVI)

VOLATILITY format
v1|VOLATILITY|PAIR|MSVI|VALUE|UNIT|30D|RV:{rv}:{w},IV:{iv}:{w},TS:{ts}:{w},FR:{fr}:{w},PCR:{pcr}:{w}|CONFIDENCE:{c}|METHOD:v{n}|{TIMESTAMP}|{NONCE}
BTC/USD example
v1|VOLATILITY|BTCUSD|MSVI|15.65|index|30D|RV:38.99:0.3,IV:38.39:0.25,TS:0.909:0.15,FR:20.02:0.2,PCR:0.651:0.1|CONFIDENCE:0.6765|METHOD:v1|1744416000|291847
MSVI differences from MSXI/MSSI

VOLATILITY canonicals include a 30D window field and omit REGIME. The UNIT field uses lowercase index. Component values use the raw value (e.g. RV = realized vol percentage), not the normalizedValue from the preview response.

The components field includes all five weights (RV, IV, TS, FR, PCR) allowing independent reconstruction and verification of the index value.

SENTIMENT Type (MSXI)

SENTIMENT format
v1|SENTIMENT|PAIR|MSXI|VALUE|INDEX|FR:{fr}:{w},SKEW:{skew}:{w},PCR:{pcr}:{w},TS:{ts}:{w},BASIS:{basis}:{w}|REGIME:{regime}|CONFIDENCE:{c}|METHOD:v{n}|{TIMESTAMP}|{NONCE}
BTC/USD example
v1|SENTIMENT|BTCUSD|MSXI|-9.88|INDEX|FR:-0.00:0.3,SKEW:-4.12:0.25,PCR:0.863:0.2,TS:0.923:0.15,BASIS:0.049:0.1|REGIME:NEUTRAL|CONFIDENCE:1.0000|METHOD:v1|1744416000|382910

Positive values indicate bullish positioning, negative values bearish. The components field includes all five weights (FR, SKEW, PCR, TS, BASIS) allowing independent reconstruction.

STRESS Type (MSSI)

STRESS format
v1|STRESS|MARKET|MSSI|VALUE|INDEX|VOL:{vol}:{w},STBL:{stbl}:{w},FR:{fr}:{w}|REGIME:{regime}|CONFIDENCE:{c}|METHOD:v{n}|{TIMESTAMP}|{NONCE}
Market example
v1|STRESS|MARKET|MSSI|64.32|INDEX|VOL:61.81:0.35,STBL:0.00:0.3,FR:38.57:0.35|REGIME:HIGH|CONFIDENCE:1.0000|METHOD:v1|1744416000|571923

Market-wide single number — not per-pair. The components field includes all three weights (VOL, STBL, FR) allowing independent reconstruction and stress source identification.

Index Numeric Precision

Component decimal places

Index values are always formatted to 2 decimal places. Component values use fixed precision per key: FR, SKEW, RV, IV, VOL, STBL use 2dp. PCR, TS use 3dp. BASIS uses 4dp. Weights are unformatted floats. Any deviation in decimal precision will cause signature verification to fail. When a component is unavailable, it is encoded as KEY:NA:weight.

Signing Process

pseudocode
canonical_bytes = canonical_string.encode("utf-8")
digest          = SHA256(canonical_bytes)
signature       = sign(digest, private_key)
output          = base64_encode(signature)
Pre-hashed signing (Ed25519ph-style)

Both protocols sign the SHA-256 digest, not the raw bytes. This is non-standard Ed25519 usage — standard Ed25519 hashes internally with SHA-512. Our implementation uses Ed25519 with an explicit SHA-256 prehash: Ed25519(SHA-256(canonical)). Most Ed25519 libraries expect raw bytes as input. You must hash first, then pass the 32-byte digest to the signing/verification function. See Signature Verification for copy-paste reference code in Python, JavaScript, and Go.

Parsing

python
fields    = canonical.split("|")
version   = fields[0]    # "v1"
type_id   = fields[1]    # "PRICE" or "ECON"
payload   = fields[2:-2] # type-specific fields
timestamp = fields[-2]   # Unix integer
nonce     = fields[-1]   # 6-digit nonce

# For PRICE type:
if type_id == "PRICE":
    pair    = fields[2]  # "BTCUSD"
    price   = fields[3]  # "84231.50"
    sources = fields[6].split(",")  # ["binance", ...]

# For VOLATILITY (no REGIME, has 30D window):
elif type_id == "VOLATILITY":
    pair       = fields[2]  # "BTCUSD"
    index_name = fields[3]  # "MSVI"
    value      = fields[4]  # index value
    unit       = fields[5]  # "index"
    window     = fields[6]  # "30D"
    components = fields[7]  # "RV:38.99:0.3,IV:38.39:0.25,..."
    confidence = fields[8].split(":")[1]  # "0.6765"

# For SENTIMENT and STRESS (have REGIME, no window):
elif type_id in ("SENTIMENT", "STRESS"):
    pair       = fields[2]  # "BTCUSD" or "MARKET"
    index_name = fields[3]  # "MSXI" or "MSSI"
    value      = fields[4]  # index value
    components = fields[6]  # "FR:-0.00:0.3,SKEW:-4.12:0.25,..."
    regime     = fields[7].split(":")[1]  # "NEUTRAL"

# For ECON (includes COMMODITIES where region="COMMODITIES"):
elif type_id == "ECON":
    region     = fields[2]  # "US", "EU", or "COMMODITIES"
    indicator  = fields[3]  # "CPI", "WTI", etc.
    value      = fields[4]
    unit       = fields[5]
    period     = fields[6]
    source     = fields[8]  # "BLS", "EIA", etc.

# GAS and WEATHER types have distinct formats — see dedicated docs.
Always use the canonical field directly

Never reconstruct the canonical string from other response fields. The nonce is server-generated and cannot be reproduced. Always verify against the canonical field returned in the response.