{
  "openapi": "3.1.0",
  "info": {
    "title": "txdecode",
    "description": "Turn any Base transaction hash into a human-readable action with normalized asset movements. x402-native: pay $0.005 per call in USDC on Base. No signup, no API keys.",
    "version": "1.0.0",
    "contact": {
      "email": "support@txdecode.com"
    }
  },
  "servers": [
    {
      "url": "https://txdecode.com"
    }
  ],
  "paths": {
    "/api/v1/decode/{txHash}": {
      "get": {
        "operationId": "decode",
        "summary": "Decode a confirmed Base transaction into a human-readable action plus normalized asset movements (token, amount, USD value). Add ?verbose=true for the full log/event breakdown.",
        "description": "Pay-per-call (x402 v2, $0.005 USDC su Base). Senza header PAYMENT-SIGNATURE risponde 402 con header PAYMENT-REQUIRED (base64 JSON) + body di cortesia. Con un pagamento valido decodifica e risponde 200 con header PAYMENT-RESPONSE. Le risposte non-200 non vengono mai settlate: gli errori non costano (§7).",
        "parameters": [
          {
            "name": "txHash",
            "in": "path",
            "required": true,
            "description": "Transaction hash: 0x + 64 hex chars. Normalizzato lowercase.",
            "schema": {
              "type": "string",
              "pattern": "^0x[0-9a-fA-F]{64}$"
            }
          },
          {
            "name": "verbose",
            "in": "query",
            "required": false,
            "description": "Include i logs[]+calldata (strict superset byte-identico). Stesso prezzo. Param sconosciuti ignorati (forward-compat).",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Transazione decodificata. Header: PAYMENT-RESPONSE (base64 SettleResponse), X-Decoder-Version, ETag W/\"<hash>:<version>\", Cache-Control private.",
            "headers": {
              "PAYMENT-RESPONSE": {
                "description": "base64 della SettleResponse x402 v2.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Decoder-Version": {
                "description": "Semver del decoder.",
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "description": "W/\"<tx_hash>:<decoder_version>\" — chiave di cache visibile.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "private, max-age=3600 (finalized) | private, max-age=30 (unfinalized).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DecodeResponse"
                }
              }
            }
          },
          "400": {
            "description": "txHash malformato (invalid-tx-hash) o verbose ≠ true/false (invalid-parameter). Prima di qualsiasi 402.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "Payment required (x402 v2): header PAYMENT-REQUIRED (base64 JSON) MANDATORIO + body di cortesia. Mai settlato.",
            "headers": {
              "PAYMENT-REQUIRED": {
                "description": "base64 JSON del PaymentRequired (fonte autoritativa).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequired"
                }
              }
            }
          },
          "404": {
            "description": "tx-not-found: hash ben formato ma ignoto agli RPC. retryable:false. Mai settlato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "tx-pending: vista in mempool ma senza receipt. Retry-After:3. Mai settlato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "rate-limited (pre-payment): stesso envelope §7. Retry-After. Mai settlato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "internal-error: details.request_id per il supporto. Mai settlato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "upstream-unavailable: tutti gli RPC della fallback chain giù. Retry-After:30. Mai settlato.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-x402": {
          "price": "0.005",
          "priceAtomic": "5000",
          "token": "USDC",
          "network": "eip155:8453",
          "facilitator": "payai",
          "headers": {
            "paymentRequired": "PAYMENT-REQUIRED",
            "paymentSignature": "PAYMENT-SIGNATURE",
            "paymentResponse": "PAYMENT-RESPONSE"
          },
          "scheme": "exact",
          "maxTimeoutSeconds": 300,
          "note": "x402 v2-only su Base. Verbose allo stesso prezzo. Le risposte non-200 non vengono mai settlate."
        }
      }
    },
    "/api/health": {
      "get": {
        "operationId": "health",
        "summary": "Liveness/readiness check.",
        "description": "Gratuito (no x402). Rate limit 60 req/min per IP hash (STANDARD §12.8 #8). Cache-Control public, max-age=10.",
        "responses": {
          "200": {
            "description": "Servizio sano.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "x-known-values": [
                        "ok",
                        "degraded"
                      ]
                    },
                    "version": {
                      "type": "string"
                    },
                    "uptime": {
                      "type": "integer",
                      "description": "Secondi."
                    },
                    "timestamp": {
                      "type": "string",
                      "description": "ISO-8601 UTC."
                    }
                  },
                  "required": [
                    "status",
                    "version",
                    "uptime",
                    "timestamp"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "rate-limited: stesso envelope §7.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "degraded: SQLite non apribile."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Asset": {
        "type": "object",
        "description": "Asset normalizzato. asset.type è enum APERTO (§8.3): valore ignoto ⇒ trattalo come unknown.",
        "properties": {
          "type": {
            "type": "string",
            "x-known-values": [
              "native",
              "erc20",
              "erc721",
              "erc1155",
              "unknown"
            ],
            "description": "Enum APERTO. 'unknown' = gamba v4 non appaiabile (amount noto dal Swap firmato, identità no)."
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Lowercase address, oppure null (es. contract deployment / gamba non risolta)."
          },
          "symbol": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "decimals": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Sempre da decimals() on-chain; mai assunti 18. null ⇒ formatted null."
          },
          "token_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stringa decimale; solo erc721/erc1155."
          }
        },
        "required": [
          "type",
          "address",
          "symbol",
          "name",
          "decimals",
          "token_id"
        ]
      },
      "Pricing": {
        "type": "object",
        "description": "Esito del pricing per gamba. status/source sono enum APERTI (§8.3).",
        "properties": {
          "status": {
            "type": "string",
            "x-known-values": [
              "ok",
              "low-confidence",
              "stale",
              "assumed-peg",
              "unpriced",
              "not-priced"
            ],
            "description": "Enum APERTO. status ignoto ⇒ trattalo come 'unpriced' (degrado conservativo). 'not-priced' = non tentato by design (NFT) ≠ 'unpriced' (tentato e fallito)."
          },
          "source": {
            "type": [
              "string",
              "null"
            ],
            "x-known-values": [
              "defillama",
              "geckoterminal",
              "assumed-peg"
            ],
            "description": "Enum APERTO. null per unpriced/not-priced."
          },
          "price_usd": {
            "type": [
              "number",
              "null"
            ]
          },
          "priced_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Unix ts del data point REALE della fonte."
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "Confidence DefiLlama; null per altre fonti."
          }
        },
        "required": [
          "status",
          "source",
          "price_usd",
          "priced_at",
          "confidence"
        ]
      },
      "ValuedAmount": {
        "type": "object",
        "description": "Importo + asset + USD + pricing, piatto (helper di action.details).",
        "properties": {
          "asset": {
            "$ref": "#/components/schemas/Asset"
          },
          "raw": {
            "type": "string",
            "description": "Atomico."
          },
          "formatted": {
            "type": [
              "string",
              "null"
            ]
          },
          "value_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "Valore USD (float64). null = non prezzato — mai 0 placeholder (invariante §8.5.2)."
          },
          "pricing": {
            "$ref": "#/components/schemas/Pricing"
          },
          "inferred": {
            "type": "boolean",
            "description": "true per gambe ricostruite (es. ETH-out)."
          }
        },
        "required": [
          "asset",
          "raw",
          "formatted",
          "value_usd",
          "pricing",
          "inferred"
        ]
      },
      "DecoderInfo": {
        "type": "object",
        "description": "Versioning e identità di cache. Presente in OGNI risposta (anche errori, senza decoded_at).",
        "properties": {
          "version": {
            "type": "string",
            "description": "Semver del decoder. Chiave cache: (tx.hash, decoder.version)."
          },
          "schema": {
            "type": "string",
            "enum": [
              "v1"
            ],
            "description": "Major dello schema — enum CHIUSO."
          },
          "decoded_at": {
            "type": "string",
            "description": "ISO-8601 UTC del calcolo (resta l'originale su cache-hit)."
          }
        },
        "required": [
          "version",
          "schema",
          "decoded_at"
        ]
      },
      "GasInfo": {
        "type": "object",
        "properties": {
          "gas_used": {
            "type": "integer"
          },
          "effective_gas_price_wei": {
            "type": "string"
          },
          "l1_fee_wei": {
            "type": "string",
            "description": "OP-stack L1 data fee; \"0\" se assente."
          },
          "total_fee_wei": {
            "type": "string"
          },
          "total_fee_eth": {
            "type": "string"
          },
          "fee_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "Valore USD (float64). null = non prezzato — mai 0 placeholder (invariante §8.5.2)."
          },
          "fee_pricing": {
            "$ref": "#/components/schemas/Pricing"
          }
        },
        "required": [
          "gas_used",
          "effective_gas_price_wei",
          "l1_fee_wei",
          "total_fee_wei",
          "total_fee_eth",
          "fee_usd",
          "fee_pricing"
        ]
      },
      "TxMeta": {
        "type": "object",
        "properties": {
          "hash": {
            "type": "string",
            "pattern": "^0x[0-9a-f]{64}$",
            "description": "Hash richiesto, lowercase."
          },
          "chain_id": {
            "type": "integer",
            "const": 8453,
            "description": "Costante: Base-only."
          },
          "network": {
            "type": "string",
            "const": "eip155:8453",
            "description": "CAIP-2 costante (lingua x402 v2)."
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "reverted"
            ],
            "description": "receipt.status — enum CHIUSO (§8.3)."
          },
          "block_number": {
            "type": "integer"
          },
          "block_hash": {
            "type": "string"
          },
          "block_timestamp": {
            "type": "integer",
            "description": "Unix seconds — àncora del pricing waterfall."
          },
          "block_timestamp_iso": {
            "type": "string"
          },
          "finality": {
            "type": "string",
            "enum": [
              "finalized",
              "unfinalized"
            ],
            "description": "Enum CHIUSO (§8.3). unfinalized = reorg teoricamente possibile (lag Base ~17-21 min)."
          },
          "from": {
            "type": "string",
            "pattern": "^0x[0-9a-f]{40}$",
            "description": "Lowercase 0x-prefixed 20-byte address (EIP-55 vietato in output)."
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "description": "Lowercase address, oppure null (es. contract deployment / gamba non risolta)."
          },
          "to_label": {
            "type": [
              "string",
              "null"
            ],
            "description": "Best-effort da tabella curata; mai da explorer non confermati."
          },
          "value": {
            "type": "object",
            "properties": {
              "raw": {
                "type": "string",
                "description": "uint256 atomico, stringa decimale (mai number)."
              },
              "formatted": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "raw / 10^decimals, stringa decimale senza separatori; null ⟺ decimals null."
              }
            },
            "required": [
              "raw",
              "formatted"
            ]
          },
          "nonce": {
            "type": "integer"
          },
          "transaction_index": {
            "type": "integer"
          },
          "gas": {
            "$ref": "#/components/schemas/GasInfo"
          },
          "explorer_url": {
            "type": "string"
          }
        },
        "required": [
          "hash",
          "chain_id",
          "network",
          "status",
          "block_number",
          "block_hash",
          "block_timestamp",
          "block_timestamp_iso",
          "finality",
          "from",
          "to",
          "to_label",
          "value",
          "nonce",
          "transaction_index",
          "gas",
          "explorer_url"
        ]
      },
      "Principal": {
        "type": "object",
        "description": "Soggetto di summary/direction/net_flows-ordering/usd.total_value_usd (§1.3).",
        "properties": {
          "address": {
            "type": "string",
            "pattern": "^0x[0-9a-f]{40}$",
            "description": "Lowercase 0x-prefixed 20-byte address (EIP-55 vietato in output)."
          },
          "basis": {
            "type": "string",
            "x-known-values": [
              "tx-sender",
              "authorizer"
            ],
            "description": "Enum APERTO. tx-sender = principal == tx.from; authorizer = settlement EIP-3009 (payer firmatario). basis ignoto ⇒ usa comunque principal.address (autoritativo)."
          }
        },
        "required": [
          "address",
          "basis"
        ]
      },
      "ActionStub": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "x-known-values": [
              "swap",
              "token-transfer",
              "token-approval"
            ]
          },
          "family": {
            "type": "string",
            "enum": [
              "transfer",
              "swap",
              "liquidity",
              "staking",
              "approval",
              "wrap",
              "payment",
              "other"
            ]
          },
          "protocols": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProtocolTag"
            }
          }
        },
        "required": [
          "type",
          "family",
          "protocols"
        ]
      },
      "ProtocolTag": {
        "type": "string",
        "description": "Tag protocollo — enum APERTO (§8.3): tag ignoto ⇒ ignoralo.",
        "x-known-values": [
          "erc20",
          "erc721",
          "erc1155",
          "weth",
          "permit2",
          "uniswap-v2",
          "uniswap-v3",
          "uniswap-v4",
          "aerodrome-vamm",
          "aerodrome-samm",
          "aerodrome-slipstream",
          "aerodrome-gauge",
          "eip-3009",
          "x402"
        ]
      },
      "Action": {
        "type": "object",
        "description": "Classificazione primaria. type APERTO, family CHIUSO: type ignoto ⇒ comportati secondo family (regola di degradazione UNICA §2.1).",
        "properties": {
          "type": {
            "type": "string",
            "description": "Enum APERTO (§2.2). type ignoto ⇒ usa family.",
            "x-known-values": [
              "native-transfer",
              "token-transfer",
              "nft-transfer",
              "token-mint",
              "token-burn",
              "nft-mint",
              "nft-burn",
              "token-approval",
              "nft-approval",
              "approval-for-all",
              "permit2-permit",
              "weth-wrap",
              "weth-unwrap",
              "swap",
              "add-liquidity",
              "remove-liquidity",
              "collect-fees",
              "gauge-stake",
              "gauge-unstake",
              "claim-rewards",
              "x402-settlement",
              "eip3009-transfer",
              "contract-deployment",
              "contract-interaction",
              "unknown"
            ]
          },
          "family": {
            "type": "string",
            "enum": [
              "transfer",
              "swap",
              "liquidity",
              "staking",
              "approval",
              "wrap",
              "payment",
              "other"
            ],
            "description": "Enum CHIUSO (catch-all 'other'). La sua modifica è MAJOR."
          },
          "protocols": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProtocolTag"
            }
          },
          "executed": {
            "type": "boolean",
            "description": "false ⟺ tx.status == 'reverted' (invariante §8.5.5)."
          },
          "description": {
            "type": "string",
            "description": "Statica per type."
          },
          "details": {
            "type": [
              "object",
              "null"
            ],
            "description": "Payload tipizzato per type (§2.3). INVARIANTE: executed == false ⇒ details == null. Per family 'payment' i path payer/pay_to/amount.raw/facilitator.* sono garantiti (invariante §8.5.6, contratto monitor)."
          },
          "secondary": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ActionStub"
            },
            "description": "Azioni co-presenti di famiglia diversa ([] se nessuna)."
          }
        },
        "required": [
          "type",
          "family",
          "protocols",
          "executed",
          "description",
          "details",
          "secondary"
        ]
      },
      "Movement": {
        "type": "object",
        "description": "Gamba lorda dai log. kind APERTO, direction CHIUSO (relativa a principal).",
        "properties": {
          "index": {
            "type": "integer",
            "description": "Posizione stabile (riferita da logs[].movement_index)."
          },
          "kind": {
            "type": "string",
            "x-known-values": [
              "transfer",
              "mint",
              "burn",
              "wrap",
              "unwrap",
              "native",
              "inferred-native",
              "swap-leg"
            ],
            "description": "Enum APERTO. kind ignoto ⇒ trattalo come 'transfer'."
          },
          "asset": {
            "$ref": "#/components/schemas/Asset"
          },
          "amount": {
            "type": "object",
            "properties": {
              "raw": {
                "type": "string",
                "description": "uint256 atomico, stringa decimale (mai number)."
              },
              "formatted": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "raw / 10^decimals, stringa decimale senza separatori; null ⟺ decimals null."
              }
            },
            "required": [
              "raw",
              "formatted"
            ]
          },
          "from": {
            "type": [
              "string",
              "null"
            ],
            "description": "Lowercase address, oppure null (es. contract deployment / gamba non risolta)."
          },
          "from_label": {
            "type": [
              "string",
              "null"
            ]
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "description": "Lowercase address, oppure null (es. contract deployment / gamba non risolta)."
          },
          "to_label": {
            "type": [
              "string",
              "null"
            ]
          },
          "direction": {
            "type": "string",
            "enum": [
              "in",
              "out",
              "self",
              "third-party"
            ],
            "description": "Enum CHIUSO, relativa a PRINCIPAL: out=from==principal; in=to==principal; self=entrambi; third-party=nessuno."
          },
          "value_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "Valore USD (float64). null = non prezzato — mai 0 placeholder (invariante §8.5.2)."
          },
          "pricing": {
            "$ref": "#/components/schemas/Pricing"
          },
          "log_index": {
            "type": [
              "integer",
              "null"
            ],
            "description": "null per kind 'native' e 'inferred-native'."
          },
          "inferred": {
            "type": "boolean",
            "description": "true SOLO per kind 'inferred-native'."
          },
          "inferred_from": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "reason": {
                "type": "string"
              },
              "log_index": {
                "type": "integer"
              }
            },
            "required": [
              "reason",
              "log_index"
            ]
          }
        },
        "required": [
          "index",
          "kind",
          "asset",
          "amount",
          "from",
          "from_label",
          "to",
          "to_label",
          "direction",
          "value_usd",
          "pricing",
          "log_index",
          "inferred",
          "inferred_from"
        ]
      },
      "Approval": {
        "type": "object",
        "description": "Approval/permit — informazione di sicurezza nel default. kind APERTO (§8.3).",
        "properties": {
          "kind": {
            "type": "string",
            "x-known-values": [
              "erc20-approval",
              "nft-approval",
              "approval-for-all",
              "permit2-permit"
            ],
            "description": "Enum APERTO. kind ignoto ⇒ trattalo come 'erc20-approval'."
          },
          "owner": {
            "type": "string",
            "pattern": "^0x[0-9a-f]{40}$",
            "description": "Lowercase 0x-prefixed 20-byte address (EIP-55 vietato in output)."
          },
          "spender": {
            "type": "string",
            "pattern": "^0x[0-9a-f]{40}$",
            "description": "Lowercase 0x-prefixed 20-byte address (EIP-55 vietato in output)."
          },
          "spender_label": {
            "type": [
              "string",
              "null"
            ]
          },
          "asset": {
            "$ref": "#/components/schemas/Asset"
          },
          "amount": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/RawFormatted"
              },
              {
                "type": "null"
              }
            ],
            "description": "null per approval-for-all."
          },
          "unlimited": {
            "type": "boolean",
            "description": "raw == max uint del tipo (uint256 / uint160 Permit2)."
          },
          "expiration": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Unix seconds; solo Permit2."
          },
          "log_index": {
            "type": "integer"
          }
        },
        "required": [
          "kind",
          "owner",
          "spender",
          "spender_label",
          "asset",
          "amount",
          "unlimited",
          "expiration",
          "log_index"
        ]
      },
      "RawFormatted": {
        "type": "object",
        "properties": {
          "raw": {
            "type": "string",
            "description": "uint256 atomico, stringa decimale (mai number)."
          },
          "formatted": {
            "type": [
              "string",
              "null"
            ],
            "description": "raw / 10^decimals, stringa decimale senza separatori; null ⟺ decimals null."
          }
        },
        "required": [
          "raw",
          "formatted"
        ]
      },
      "NetFlowDelta": {
        "type": "object",
        "properties": {
          "asset": {
            "$ref": "#/components/schemas/Asset"
          },
          "amount": {
            "type": "object",
            "description": "CON SEGNO: raw negativo = outflow.",
            "properties": {
              "raw": {
                "type": "string"
              },
              "formatted": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "raw",
              "formatted"
            ]
          },
          "value_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "CON SEGNO; null se unpriced."
          },
          "price_status": {
            "type": "string",
            "x-known-values": [
              "ok",
              "low-confidence",
              "stale",
              "assumed-peg",
              "unpriced",
              "not-priced"
            ],
            "description": "Slim: il Pricing completo vive sul movement corrispondente."
          }
        },
        "required": [
          "asset",
          "amount",
          "value_usd",
          "price_status"
        ]
      },
      "NetFlow": {
        "type": "object",
        "description": "Delta-netting per address. Il principal è SEMPRE il primo elemento quando ha delta ≠ 0. L'address 0x0 NON compare mai (regola di fold §3).",
        "properties": {
          "address": {
            "type": "string",
            "pattern": "^0x[0-9a-f]{40}$",
            "description": "Lowercase 0x-prefixed 20-byte address (EIP-55 vietato in output)."
          },
          "address_label": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_principal": {
            "type": "boolean"
          },
          "deltas": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NetFlowDelta"
            }
          }
        },
        "required": [
          "address",
          "address_label",
          "is_principal",
          "deltas"
        ]
      },
      "UsdTotals": {
        "type": "object",
        "description": "Aggregati USD null-severi (§1.5). null se ANCHE UNA gamba della basis è unpriced (mai sommare attorno ai buchi).",
        "properties": {
          "total_value_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "Valore USD (float64). null = non prezzato — mai 0 placeholder (invariante §8.5.2)."
          },
          "basis": {
            "type": [
              "string",
              "null"
            ],
            "x-known-values": [
              "transfer-amount",
              "swap-input-leg",
              "settlement-amount",
              "wrap-amount",
              "liquidity-leg-sum"
            ],
            "description": "Enum APERTO. COSA misura total_value_usd. null ⟺ total_value_usd null o azione senza valore naturale. basis ignoto ⇒ usa il totale solo come informativo."
          },
          "total_price_status": {
            "type": [
              "string",
              "null"
            ],
            "x-known-values": [
              "ok",
              "low-confidence",
              "stale",
              "assumed-peg",
              "unpriced",
              "not-priced"
            ],
            "description": "Status PEGGIORE tra le gambe della basis. null ⟺ total_value_usd null."
          },
          "priced_movements": {
            "type": "integer"
          },
          "unpriced_movements": {
            "type": "integer",
            "description": "Gambe con status == 'unpriced' (NON conta 'not-priced')."
          }
        },
        "required": [
          "total_value_usd",
          "basis",
          "total_price_status",
          "priced_movements",
          "unpriced_movements"
        ]
      },
      "CompletenessFlag": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Enum APERTO (kebab-case). code ignoto ⇒ trattalo secondo severity.",
            "x-known-values": [
              "native-eth-out-inferred",
              "internal-eth-possible",
              "v4-tokens-resolved-by-pairing",
              "v4-native-leg-invisible",
              "v4-pool-tokens-unresolved",
              "uninterpreted-logs",
              "unknown-token-decimals",
              "unverified-emitter",
              "reverted-no-logs",
              "unfinalized-block",
              "fee-on-transfer-detected",
              "price-assumed-peg"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "info",
              "data-gap"
            ],
            "description": "Enum CHIUSO. 'info' = dato ricostruito/contestuale; 'data-gap' = informazione che esiste on-chain ma l'engine non può vederla."
          },
          "message": {
            "type": "string",
            "description": "Inglese, una frase — non parsare."
          },
          "log_indices": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "integer"
            }
          }
        },
        "required": [
          "code",
          "severity",
          "message",
          "log_indices"
        ]
      },
      "Completeness": {
        "type": "object",
        "description": "Fedeltà della decodifica. engine/native_eth_visibility COSTANTI per tutta la vita di /v1 (invariante §8.5.8).",
        "properties": {
          "engine": {
            "type": "string",
            "const": "logs-only",
            "description": "COSTANTE (vincolo architetturale nel contratto)."
          },
          "native_eth_visibility": {
            "type": "string",
            "const": "tx-value-and-weth-events-only",
            "description": "COSTANTE: internal ETH transfer strutturalmente invisibili."
          },
          "decoded_logs": {
            "type": "integer",
            "description": "Log TOTALI nel receipt."
          },
          "interpreted_logs": {
            "type": "integer"
          },
          "uninterpreted_logs": {
            "type": "integer",
            "description": "INVARIANTE §8.5.4: decoded_logs == interpreted_logs + uninterpreted_logs."
          },
          "level": {
            "type": "string",
            "enum": [
              "full",
              "partial",
              "minimal",
              "intent-only"
            ],
            "description": "Enum CHIUSO — gate a confronto singolo. Mappa deterministica §6."
          },
          "flags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompletenessFlag"
            }
          }
        },
        "required": [
          "engine",
          "native_eth_visibility",
          "decoded_logs",
          "interpreted_logs",
          "uninterpreted_logs",
          "level",
          "flags"
        ]
      },
      "DecodedLog": {
        "type": "object",
        "description": "SOLO con ?verbose=true (§5). Strict superset byte-identico al default (invariante §8.5.9).",
        "properties": {
          "log_index": {
            "type": "integer"
          },
          "address": {
            "type": "string",
            "pattern": "^0x[0-9a-f]{40}$",
            "description": "Lowercase 0x-prefixed 20-byte address (EIP-55 vietato in output)."
          },
          "address_label": {
            "type": [
              "string",
              "null"
            ]
          },
          "topics": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Raw, sempre presenti — verità non perdibile."
          },
          "data": {
            "type": "string",
            "description": "Raw hex integrale."
          },
          "event": {
            "type": [
              "object",
              "null"
            ],
            "description": "null = topic0 irrisolvibile.",
            "properties": {
              "name": {
                "type": "string"
              },
              "signature": {
                "type": "string"
              },
              "args": {
                "type": "object",
                "description": "Valori SEMPRE stringhe (int firmati con '-': qui e SOLO qui i segni raw v3/v4); address lowercase.",
                "additionalProperties": {
                  "type": [
                    "string",
                    "boolean"
                  ]
                }
              },
              "abi_source": {
                "type": "string",
                "x-known-values": [
                  "curated",
                  "cache",
                  "sourcify",
                  "etherscan",
                  "signature-db"
                ],
                "description": "Enum APERTO."
              }
            },
            "required": [
              "name",
              "signature",
              "args",
              "abi_source"
            ]
          },
          "interpretation": {
            "type": "string",
            "x-known-values": [
              "movement",
              "approval",
              "action-evidence",
              "skipped",
              "unknown"
            ],
            "description": "Enum APERTO. 'unknown' conta in uninterpreted_logs."
          },
          "movement_index": {
            "type": [
              "integer",
              "null"
            ]
          },
          "approval_index": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "log_index",
          "address",
          "address_label",
          "topics",
          "data",
          "event",
          "interpretation",
          "movement_index",
          "approval_index"
        ]
      },
      "CalldataInfo": {
        "type": "object",
        "description": "SOLO con ?verbose=true (§5).",
        "properties": {
          "selector": {
            "type": "string",
            "description": "\"0x\" per puro value transfer."
          },
          "function": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "signature": {
                "type": "string"
              },
              "args": {
                "type": "object",
                "additionalProperties": {
                  "type": [
                    "string",
                    "boolean"
                  ]
                }
              }
            },
            "required": [
              "name",
              "signature",
              "args"
            ]
          },
          "abi_source": {
            "type": [
              "string",
              "null"
            ],
            "x-known-values": [
              "curated",
              "cache",
              "sourcify",
              "etherscan",
              "signature-db"
            ]
          },
          "note": {
            "type": "string",
            "description": "Fisso: 'intent-only: amounts in this section come from calldata and reflect intent, not outcome; authoritative amounts are in movements[]'."
          },
          "raw": {
            "type": "string",
            "description": "Input integrale, suffissi arbitrari inclusi."
          }
        },
        "required": [
          "selector",
          "function",
          "abi_source",
          "note",
          "raw"
        ]
      },
      "DecodeResponse": {
        "type": "object",
        "description": "Risposta 200 di GET /api/v1/decode/{txHash}. I 10 campi di §1 sono SEMPRE presenti (invariante §8.5.1). logs/calldata SOLO con ?verbose=true (strict superset, §5).",
        "properties": {
          "decoder": {
            "$ref": "#/components/schemas/DecoderInfo"
          },
          "tx": {
            "$ref": "#/components/schemas/TxMeta"
          },
          "principal": {
            "$ref": "#/components/schemas/Principal"
          },
          "action": {
            "$ref": "#/components/schemas/Action"
          },
          "summary": {
            "type": "string",
            "description": "Una frase, soggetto = principal. NON parsare (§4)."
          },
          "movements": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Movement"
            }
          },
          "approvals": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Approval"
            }
          },
          "net_flows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NetFlow"
            }
          },
          "usd": {
            "$ref": "#/components/schemas/UsdTotals"
          },
          "completeness": {
            "$ref": "#/components/schemas/Completeness"
          },
          "logs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DecodedLog"
            },
            "description": "SOLO con ?verbose=true."
          },
          "calldata": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CalldataInfo"
              }
            ],
            "description": "SOLO con ?verbose=true."
          }
        },
        "required": [
          "decoder",
          "tx",
          "principal",
          "action",
          "summary",
          "movements",
          "approvals",
          "net_flows",
          "usd",
          "completeness"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "description": "Envelope uniforme per TUTTI i non-2xx (§7). Discriminato dalla chiave 'error'. Il 402 NON usa questo envelope (segue il PaymentRequired del protocollo x402).",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Enum APERTO, kebab-case, mai rinominato. code ignoto ⇒ decidi su http_status + retryable.",
                "x-known-values": [
                  "invalid-tx-hash",
                  "invalid-parameter",
                  "tx-not-found",
                  "tx-pending",
                  "rate-limited",
                  "upstream-unavailable",
                  "internal-error"
                ]
              },
              "http_status": {
                "type": "integer",
                "description": "Ridondante con la status line — self-describing fuori dal contesto HTTP."
              },
              "message": {
                "type": "string",
                "description": "Inglese, actionable; include il valore offensivo se utile."
              },
              "details": {
                "type": "object",
                "description": "Payload per codice, additivo."
              },
              "retryable": {
                "type": "boolean",
                "description": "La STESSA richiesta può riuscire più tardi?"
              },
              "retry_after_seconds": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Sempre nel body (oltre all'header Retry-After quando non-null)."
              },
              "docs": {
                "type": "string",
                "description": "https://txdecode.com/docs/errors#<code>"
              }
            },
            "required": [
              "code",
              "http_status",
              "message",
              "details",
              "retryable",
              "retry_after_seconds",
              "docs"
            ]
          },
          "decoder": {
            "type": "object",
            "properties": {
              "version": {
                "type": "string"
              },
              "schema": {
                "type": "string",
                "enum": [
                  "v1"
                ]
              }
            },
            "required": [
              "version",
              "schema"
            ]
          }
        },
        "required": [
          "error",
          "decoder"
        ]
      },
      "PaymentRequired": {
        "type": "object",
        "description": "Body di cortesia del 402 (x402 v2). La fonte autoritativa è l'header PAYMENT-REQUIRED (base64 JSON, MANDATORIO sul 402). Tipi @x402 opachi al boundary (DESIGN §5.10): MAI rivalidati col root zod. additionalProperties aperto.",
        "properties": {
          "x402Version": {
            "type": "integer",
            "description": "Versione protocollo x402 (2)."
          },
          "error": {
            "type": "string"
          },
          "accepts": {
            "type": "array",
            "description": "PaymentRequirements accettati (hand-built per-network).",
            "items": {
              "type": "object",
              "properties": {
                "scheme": {
                  "type": "string",
                  "description": "\"exact\"."
                },
                "network": {
                  "type": "string",
                  "description": "CAIP-2, es. eip155:8453 / eip155:84532."
                },
                "asset": {
                  "type": "string",
                  "description": "Indirizzo del token USDC sul network."
                },
                "amount": {
                  "type": "string",
                  "description": "Atomico USDC (6 dec): \"5000\" = $0.005."
                },
                "payTo": {
                  "type": "string",
                  "description": "Receiving wallet del progetto."
                },
                "maxTimeoutSeconds": {
                  "type": "integer",
                  "description": "300."
                },
                "extra": {
                  "type": "object",
                  "description": "Per-network: mainnet {name:\"USD Coin\",version:\"2\"}, Sepolia {name:\"USDC\",version:\"2\"}."
                }
              }
            }
          },
          "resource": {
            "type": "object",
            "description": "ResourceInfo (url, description, ...)."
          }
        }
      }
    }
  },
  "x-stability": {
    "note": "Invarianti normativi del contratto di risposta (response-schema.md §8.5). Validi per tutta la vita di /api/v1; modificarne uno = MAJOR (nuovo path /api/v2).",
    "decoder_version": "1.0.0",
    "schema_major": "v1",
    "enum_policy": "Enum CHIUSI usano `enum:` (modifica = MAJOR). Enum APERTI usano `type: string` + `x-known-values` e MAI `enum:`: un valore nuovo è MINOR. `additionalProperties` mai `false` sugli oggetti del contratto.",
    "closed_enums": [
      "tx.status",
      "tx.finality",
      "action.family",
      "movements[].direction",
      "completeness.level",
      "completeness.flags[].severity",
      "hops[].legs_resolved",
      "decoder.schema"
    ],
    "open_enums": [
      "action.type",
      "action.protocols[]",
      "principal.basis",
      "asset.type",
      "movements[].kind",
      "pricing.status",
      "pricing.source",
      "usd.basis",
      "approvals[].kind",
      "authorization.kind",
      "hops[].resolution",
      "completeness.flags[].code",
      "logs[].interpretation",
      "logs[].event.abi_source",
      "error.code"
    ],
    "invariants": [
      "I 10 campi top-level (decoder, tx, principal, action, summary, movements, approvals, net_flows, usd, completeness) sono sempre presenti in ogni 200; mai rimossi/rinominati in /v1.",
      "value_usd === null ⟺ pricing.status ∈ {unpriced, not-priced}. Mai 0 placeholder.",
      "usd.total_value_usd !== null ⇒ usd.unpriced_movements === 0 E basis/total_price_status non-null.",
      "decoded_logs === interpreted_logs + uninterpreted_logs.",
      "action.executed === false ⟺ tx.status === 'reverted' ⟺ completeness.level === 'intent-only'; e allora action.details === null e movements === [].",
      "Contratto monitor (§2.3): con action.family == 'payment' e executed == true, i path details.payer, details.pay_to, details.amount.raw, details.facilitator.address, details.facilitator.known_x402_signer sono garantiti, identici per eip3009-transfer e x402-settlement.",
      "Tutti gli address lowercase, tutti gli hash lowercase, in ogni campo, verbose incluso.",
      "completeness.engine === 'logs-only' e native_eth_visibility === 'tx-value-and-weth-events-only' costanti per tutta la vita di /v1.",
      "La risposta ?verbose=true è uno strict superset byte-identico del default (stessi byte sui campi condivisi + logs[]/calldata)."
    ],
    "client_rules": [
      "Ignora i campi che non conosci.",
      "action.type ignoto ⇒ comportati secondo action.family.",
      "completeness.flags[].code ignoto ⇒ trattalo secondo severity.",
      "pricing.status ignoto ⇒ trattalo come unpriced.",
      "Non parsare summary: ogni numero che contiene esiste in un campo tipizzato.",
      "Una risposta è un errore ⟺ contiene la chiave 'error'.",
      "Le risposte non-200 non sono mai state pagate (gli errori non costano $0.005)."
    ],
    "cache": {
      "key": "(tx_hash, decoder.version)",
      "etag": "W/\"<tx_hash>:<decoder_version>\"",
      "cache_control_finalized": "private, max-age=3600",
      "cache_control_unfinalized": "private, max-age=30",
      "errors": "no-store",
      "note": "Mai `public`/`immutable`: l'URL non è versionato e le risposte sono pagate per-request (x402) — una shared cache le leakerebbe o congelerebbe un decode pre-bump."
    }
  }
}
