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.
VERSION | TYPE | <payload fields...> | TIMESTAMP | NONCE
Envelope Fields
| Field | Position | Type | Example | Description |
|---|---|---|---|---|
| VERSION | 0 | string | v1 | Format version. |
| TYPE | 1 | string | PRICE, ECON, VOLATILITY, SENTIMENT, STRESS, GAS, GAS_INDEX, WEATHER | Attestation type — determines payload structure. COMMODITIES use TYPE=ECON with REGION=COMMODITIES. |
| TIMESTAMP | N-1 | integer | 1741514400 | Unix timestamp (seconds). Always second-to-last. |
| NONCE | N | integer | 482910 | 6-digit random nonce. Always last field. |
PRICE Type
v1|PRICE|PAIR|PRICE_VALUE|CURRENCY|DECIMALS|SOURCES|METHOD|TIMESTAMP|NONCE
v1|PRICE|BTCUSD|84231.50|USD|2|binance,binance_us,bitfinex,bitstamp,coinbase,gateio,gemini,kraken,okx|median|1741514400|482910
v1|PRICE|EURUSD|1.15000|USD|5|bankofcanada,bitstamp,cnb,ecb,ecbdirect,kraken,norgesbank|median|1741514400|719304
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
v1|ECON|REGION|INDICATOR|VALUE|UNIT|PERIOD|VINTAGEDATE|SOURCEAGENCY|SERIESID|SOURCEMODEL|TIMESTAMP|NONCE
v1|ECON|US|CPI|326.785|index198284100|2026-02|2026-03-14|BLS|CUUR0000SA0|directapi|1741514400|830114
COMMODITIES (ECON subtype)
v1|ECON|COMMODITIES|SYMBOL|VALUE|UNIT|PERIOD|VINTAGEDATE|SOURCEAGENCY|SERIESID|directapi|TIMESTAMP|NONCE
v1|ECON|COMMODITIES|WTI|94.65|usdperbarrel|2026-03-09|2026-03-15|EIA|DCOILWTICO|directapi|1741514400|402341
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)
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}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
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)
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}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)
v1|STRESS|MARKET|MSSI|VALUE|INDEX|VOL:{vol}:{w},STBL:{stbl}:{w},FR:{fr}:{w}|REGIME:{regime}|CONFIDENCE:{c}|METHOD:v{n}|{TIMESTAMP}|{NONCE}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
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
canonical_bytes = canonical_string.encode("utf-8")
digest = SHA256(canonical_bytes)
signature = sign(digest, private_key)
output = base64_encode(signature)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
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.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.