{
  "openapi": "3.1.0",
  "info": {
    "title": "RiskRouter API",
    "version": "1.0.0",
    "summary": "Deterministic micro-insurance pricing and a tamper-evident, independently verifiable audit ledger for licensed insurance distributors.",
    "description": "RiskRouter is infrastructure for insurance distributors: it prices a configuration server-side and, when a distributor key is presented, records the quote in an append-only, SHA-256 hash-chained ledger whose head is signed (ECDSA P-256) so anyone can verify it offline.\n\nStatus, stated plainly: this is a validation build. Prices come from a published mock rating matrix, no carrier, bank or payment rail is contacted, and no insurance cover is in force. RiskRouter does not sell insurance and never contacts the policyholder.\n\nPricing needs no authentication. Recording needs a key; a sandbox key can be taken on the spot from POST /api/v1/sandbox/keys. Keys are sent as `Authorization: Bearer <key>` or `X-RiskRouter-Key: <key>`. Only the SHA-256 of a key is ever stored.\n\nHuman-readable reference: https://riskrouter.eu/docs. Offline verifier: https://github.com/aniljangrabe-dev/riskrouter-verify.",
    "contact": {
      "name": "RiskRouter",
      "url": "https://riskrouter.eu/contact"
    }
  },
  "servers": [
    {
      "url": "https://api.riskrouter.eu"
    }
  ],
  "externalDocs": {
    "description": "Integration guide",
    "url": "https://riskrouter.eu/integrate"
  },
  "tags": [
    {
      "name": "Pricing"
    },
    {
      "name": "Ledger",
      "description": "The append-only audit ledger and the material to verify it without trusting us."
    },
    {
      "name": "Keys"
    },
    {
      "name": "Service"
    },
    {
      "name": "Evidence",
      "description": "v2: digest-only evidence of any regulated decision, in an RFC 6962 Merkle tree."
    }
  ],
  "paths": {
    "/api/v1/quote": {
      "post": {
        "tags": [
          "Pricing"
        ],
        "operationId": "priceQuote",
        "summary": "Price a configuration, and record it when a key is presented",
        "description": "The server prices every quote from its own matrix and ignores any price a client sends. Mandatory base layers are always included. Without a key the quote is priced and nothing is written. With a valid key it is also appended to the ledger; check `persistence.persisted` (or `attribution.recorded`), because a 200 prices the quote even when the ledger refused the write. An invalid, revoked or suspended key is refused with 401 and never downgraded to anonymous.",
        "security": [
          {},
          {
            "bearerKey": []
          },
          {
            "headerKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "example": {
                "active_vertical": "mobility",
                "selected_components": {
                  "commuter_roadside": true
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Priced. Whether it was recorded is in `persistence` and `attribution`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResponse"
                }
              }
            },
            "headers": {
              "Idempotent-Replayed": {
                "description": "Present and 'true' when this response replays an entry recorded by an earlier request with the same Idempotency-Key.",
                "schema": {
                  "type": "string",
                  "const": "true"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Recommended on every recorded quote. The same key from the same distributor records at most one ledger entry; a retry returns that entry with persistence.replayed: true and an Idempotent-Replayed: true header. Reusing a key for a different quote returns 409. Ignored on anonymous quotes.",
            "schema": {
              "type": "string",
              "pattern": "^[!-~]{1,255}$"
            }
          }
        ]
      }
    },
    "/api/v1/sandbox/keys": {
      "post": {
        "tags": [
          "Keys"
        ],
        "operationId": "issueSandboxKey",
        "summary": "Issue a self-serve sandbox key, shown once",
        "description": "No account and nothing about the caller is stored. The key is returned once; only its SHA-256 digest is kept. Limits are enforced by the database: 50 recorded entries per key per day and a shared hourly allowance of new keys. Sandbox only.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "acknowledge_permanent"
                ],
                "properties": {
                  "acknowledge_permanent": {
                    "type": "boolean",
                    "const": true,
                    "description": "Entries recorded with the key are written to an append-only ledger and can never be deleted. Must be the boolean true."
                  },
                  "src": {
                    "type": "string",
                    "description": "Optional campaign tag of the link the visitor arrived through. A malformed tag is dropped, never refused."
                  }
                }
              },
              "example": {
                "acknowledge_permanent": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Issued. The key appears in this response and nowhere else.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SandboxKey"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/verticals": {
      "get": {
        "tags": [
          "Pricing"
        ],
        "operationId": "listVerticals",
        "summary": "The published rating matrix",
        "description": "Every vertical, its mandatory base layer and optional add-ons, with monthly premiums in EUR as two-decimal strings. The single source of truth a UI should render from.",
        "responses": {
          "200": {
            "description": "The catalogue.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Verticals"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ledger/attestation": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "getAttestation",
        "summary": "Signed entry count and head hash",
        "description": "Save the whole response. The signature covers `riskrouter-ledger-attestation|v1|as_of|entries|head_hash`, so none of the three can be changed afterwards without it failing.",
        "responses": {
          "200": {
            "description": "The current head, signed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Attestation"
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/ledger/export": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "exportLedger",
        "summary": "Your entries, the chain skeleton, and the signed head",
        "description": "Everything needed to verify offline, forever: your own entries in full, (chain_index, row_hash, prev_hash) for every entry in the ledger, and the signed attestation. Check it at https://riskrouter.eu/verify#check or with the offline verifier.",
        "security": [
          {
            "bearerKey": []
          },
          {
            "headerKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "The export.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LedgerExport"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/ledger/pubkey": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "getPublicKey",
        "summary": "The public half of the attestation key",
        "description": "Convenience only. Verify against the copy in the public repository (anchors/signing-key.json) or one you saved earlier, not a key handed to you at the moment you check.",
        "responses": {
          "200": {
            "description": "The key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicKey"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/contact": {
      "post": {
        "tags": [
          "Service"
        ],
        "operationId": "sendEnquiry",
        "summary": "Submit a business enquiry (the site's contact form)",
        "description": "Listed because it is a public write endpoint, not because it is part of the integration surface. Enquiries hold personal data, are erasable, and are deleted twelve months after submission.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "email",
                  "message"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "organisation": {
                    "type": "string",
                    "maxLength": 160
                  },
                  "enquiry_type": {
                    "type": "string",
                    "enum": [
                      "pilot",
                      "integration",
                      "compliance",
                      "other"
                    ],
                    "default": "other"
                  },
                  "message": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 4000
                  },
                  "src": {
                    "type": "string",
                    "description": "Optional campaign tag. Dropped if malformed."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Stored.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "received": {
                      "type": "boolean"
                    },
                    "reference": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/health": {
      "get": {
        "tags": [
          "Service"
        ],
        "operationId": "getHealth",
        "summary": "Liveness and binding readiness",
        "description": "`binding_configured: false` means quotes still price but nothing is recorded.",
        "responses": {
          "200": {
            "description": "Status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/landings": {
      "post": {
        "tags": [
          "Service"
        ],
        "operationId": "countLanding",
        "summary": "Count an arrival from a tagged campaign link (the site's own measurement)",
        "description": "Adds one to a daily count per campaign tag and page. Nothing else is read or stored: no IP, no identifier, no browser details. Not part of the integration surface.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "src",
                  "path"
                ],
                "properties": {
                  "src": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9-]{0,39}$"
                  },
                  "path": {
                    "type": "string",
                    "example": "/integrate"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Counted."
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v2/evidence": {
      "post": {
        "tags": [
          "Evidence"
        ],
        "operationId": "recordEvidence",
        "summary": "Record the digest of any regulated decision",
        "description": "The client hashes its own salted record and sends only the SHA-256; the record never reaches RiskRouter. The entry becomes a leaf of an RFC 6962 Merkle tree. A refused write answers 503 with recorded: false, never a success. Specification: https://riskrouter.eu/spec",
        "security": [
          {
            "bearerKey": []
          },
          {
            "headerKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "record_digest",
                  "kind"
                ],
                "additionalProperties": false,
                "properties": {
                  "record_digest": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{64}$"
                  },
                  "kind": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9.-]{0,39}$"
                  }
                }
              },
              "example": {
                "record_digest": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                "kind": "ai.decision"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "recorded": {
                      "type": "boolean",
                      "const": true
                    },
                    "entry": {
                      "$ref": "#/components/schemas/EvidenceEntry"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v2/evidence/head": {
      "get": {
        "tags": [
          "Evidence"
        ],
        "operationId": "evidenceHead",
        "summary": "The signed tree head of the evidence log",
        "description": "Save it: every later head must be consistent with it.",
        "responses": {
          "200": {
            "description": "The signed head.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignedEvidenceHead"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v2/evidence/proof": {
      "get": {
        "tags": [
          "Evidence"
        ],
        "operationId": "evidenceProof",
        "summary": "Inclusion proof for one of your entries",
        "description": "RFC 9162 audit path from the entry's leaf hash to the root of a tree of tree_size leaves. Take tree_size and root_hash from the same signed head.",
        "security": [
          {
            "bearerKey": []
          },
          {
            "headerKey": []
          }
        ],
        "parameters": [
          {
            "name": "leaf_index",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "tree_size",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The proof.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "format": {
                      "type": "string",
                      "const": "riskrouter-evidence-proof|v2"
                    },
                    "entry": {
                      "$ref": "#/components/schemas/EvidenceEntry"
                    },
                    "tree_size": {
                      "type": "integer"
                    },
                    "audit_path": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "pattern": "^[0-9a-f]{64}$"
                      }
                    },
                    "head": {
                      "$ref": "#/components/schemas/EvidenceHead"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v2/evidence/consistency": {
      "get": {
        "tags": [
          "Evidence"
        ],
        "operationId": "evidenceConsistency",
        "summary": "Proof that an earlier head is a prefix of a later one",
        "description": "RFC 9162 consistency proof. Check it against the root in the head you saved for first, never a root the service supplies.",
        "parameters": [
          {
            "name": "first",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "second",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The proof.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "format": {
                      "type": "string",
                      "const": "riskrouter-evidence-consistency|v2"
                    },
                    "first": {
                      "type": "integer"
                    },
                    "second": {
                      "type": "integer"
                    },
                    "proof": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "pattern": "^[0-9a-f]{64}$"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "A RiskRouter API key, e.g. rr_sandbox_…"
      },
      "headerKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-RiskRouter-Key"
      }
    },
    "responses": {
      "Error": {
        "description": "Every error carries the same envelope with a stable code.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Money": {
        "type": "string",
        "pattern": "^\\d+\\.\\d{2}$",
        "description": "EUR, exactly two decimals, as a string. Never a float.",
        "examples": [
          "3.00"
        ]
      },
      "QuoteRequest": {
        "type": "object",
        "required": [
          "active_vertical"
        ],
        "properties": {
          "active_vertical": {
            "type": "string",
            "enum": [
              "mobility",
              "digital_nomad",
              "urban_rental",
              "micro_travel"
            ]
          },
          "selected_components": {
            "type": "object",
            "additionalProperties": {
              "type": "boolean"
            },
            "description": "Add-on code to boolean. Unknown codes are reported in `ignored_components`, never priced. Omit for base layer only."
          }
        }
      },
      "QuoteResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "quote": {
            "type": "object",
            "properties": {
              "quote_reference": {
                "type": "string",
                "format": "uuid"
              },
              "active_vertical": {
                "type": "string"
              },
              "currency": {
                "type": "string",
                "const": "EUR"
              },
              "billing_interval": {
                "type": "string",
                "const": "MONTHLY"
              },
              "selected_components": {
                "type": "object",
                "additionalProperties": {
                  "type": "boolean"
                }
              },
              "ignored_components": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "line_items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              },
              "totals": {
                "type": "object",
                "properties": {
                  "base_layer_premium": {
                    "$ref": "#/components/schemas/Money"
                  },
                  "add_on_premium": {
                    "$ref": "#/components/schemas/Money"
                  },
                  "total_monthly_premium": {
                    "$ref": "#/components/schemas/Money"
                  },
                  "indicative_annual_premium": {
                    "$ref": "#/components/schemas/Money"
                  },
                  "active_add_on_count": {
                    "type": "integer"
                  }
                }
              }
            }
          },
          "attribution": {
            "type": "object",
            "properties": {
              "recorded": {
                "type": "boolean",
                "description": "True only when the ledger accepted the entry."
              },
              "mode": {
                "type": "string",
                "enum": [
                  "ANONYMOUS",
                  "ATTRIBUTED"
                ]
              },
              "distributor": {
                "type": "string"
              },
              "environment": {
                "type": "string",
                "enum": [
                  "sandbox",
                  "production"
                ]
              },
              "note": {
                "type": "string"
              }
            }
          },
          "persistence": {
            "type": "object",
            "properties": {
              "attempted": {
                "type": "boolean"
              },
              "persisted": {
                "type": "boolean"
              },
              "record_id": {
                "type": "string"
              },
              "seal": {
                "type": "object",
                "properties": {
                  "chain_index": {
                    "type": "integer"
                  },
                  "row_hash": {
                    "type": "string"
                  },
                  "prev_hash": {
                    "type": "string"
                  }
                }
              },
              "detail": {
                "type": "string"
              },
              "replayed": {
                "type": "boolean",
                "description": "True when an earlier request with the same Idempotency-Key recorded this entry; nothing new was written."
              },
              "idempotency_key": {
                "type": "string"
              }
            }
          },
          "compliance": {
            "type": "object",
            "additionalProperties": true
          },
          "engine": {
            "type": "object",
            "additionalProperties": true
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SandboxKey": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "key": {
            "type": "string",
            "pattern": "^rr_sandbox_[0-9a-f]{48}$"
          },
          "key_prefix": {
            "type": "string"
          },
          "environment": {
            "type": "string",
            "const": "sandbox"
          },
          "distributor": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "slug": {
                "type": "string"
              }
            }
          },
          "warning": {
            "type": "string"
          },
          "limits": {
            "type": "object",
            "properties": {
              "ledger_entries_per_day": {
                "type": "integer"
              },
              "production": {
                "type": "string"
              }
            }
          },
          "next_steps": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "step": {
                  "type": "string"
                },
                "command": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                }
              }
            }
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Verticals": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "verticals": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "base": {
                  "$ref": "#/components/schemas/Component"
                },
                "addons": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Component"
                  }
                }
              }
            }
          }
        }
      },
      "Component": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          },
          "monthly_premium": {
            "$ref": "#/components/schemas/Money"
          },
          "mandatory": {
            "type": "boolean"
          }
        }
      },
      "Attestation": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "attestation": {
            "type": "object",
            "properties": {
              "as_of": {
                "type": "string",
                "format": "date-time"
              },
              "entries": {
                "type": "integer"
              },
              "genesis": {
                "type": "string"
              },
              "algorithm": {
                "type": "string",
                "const": "sha256"
              },
              "canonical": {
                "type": "string"
              },
              "head_hash": {
                "type": "string",
                "pattern": "^[0-9a-f]{64}$"
              }
            }
          },
          "signature": {
            "type": "object",
            "properties": {
              "algorithm": {
                "type": "string",
                "const": "ECDSA_P256_SHA256"
              },
              "key_id": {
                "type": "string"
              },
              "signed_payload": {
                "type": "string"
              },
              "signature": {
                "type": "string",
                "contentEncoding": "base64"
              }
            }
          },
          "signature_unavailable": {
            "type": "string",
            "description": "Present instead of `signature` when no signing key is bound. Never a fabricated signature."
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LedgerExport": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "const": "riskrouter-ledger-export|v1"
          },
          "attestation": {
            "$ref": "#/components/schemas/Attestation/properties/attestation"
          },
          "signature": {
            "$ref": "#/components/schemas/Attestation/properties/signature"
          },
          "counts": {
            "type": "object",
            "properties": {
              "chain": {
                "type": "integer"
              },
              "yours": {
                "type": "integer"
              }
            }
          },
          "skeleton": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "chain_index": {
                  "type": "integer"
                },
                "row_hash": {
                  "type": "string"
                },
                "prev_hash": {
                  "type": "string"
                }
              }
            }
          },
          "entries": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "PublicKey": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "key_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "algorithm": {
            "type": "string"
          },
          "public_key": {
            "type": "object",
            "properties": {
              "kty": {
                "const": "EC"
              },
              "crv": {
                "const": "P-256"
              },
              "x": {
                "type": "string"
              },
              "y": {
                "type": "string"
              }
            }
          },
          "caveat": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "docs"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "docs": {
                "type": "string",
                "format": "uri",
                "description": "The row of the error table on https://riskrouter.eu/docs that explains this code and what to do about it."
              },
              "details": {}
            }
          }
        }
      },
      "EvidenceEntry": {
        "type": "object",
        "required": [
          "leaf_index",
          "created_at",
          "distributor_id",
          "kind",
          "record_digest",
          "leaf_hash"
        ],
        "properties": {
          "leaf_index": {
            "type": "integer",
            "minimum": 0
          },
          "created_at": {
            "type": "string",
            "description": "UTC, YYYY-MM-DDTHH:MM:SS.ffffffZ, exactly as in the leaf string."
          },
          "distributor_id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9.-]{0,39}$"
          },
          "record_digest": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "leaf_hash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "SHA-256(0x00 || riskrouter-evidence-leaf|v2|leaf_index|created_at|distributor_id|kind|record_digest)"
          }
        }
      },
      "EvidenceHead": {
        "type": "object",
        "required": [
          "tree_size",
          "root_hash",
          "timestamp"
        ],
        "properties": {
          "tree_size": {
            "type": "integer",
            "minimum": 0
          },
          "root_hash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SignedEvidenceHead": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "format": {
            "type": "string",
            "const": "riskrouter-evidence-head|v2"
          },
          "head": {
            "$ref": "#/components/schemas/EvidenceHead"
          },
          "signature": {
            "type": "object",
            "description": "ECDSA P-256 over riskrouter-evidence-head|v2|tree_size|root_hash|timestamp. Absent, with signature_unavailable, when no key is bound."
          },
          "signature_unavailable": {
            "type": "string"
          }
        }
      }
    }
  }
}
