{
  "openapi": "3.1.0",
  "info": {
    "title": "nano-monitor API",
    "version": "1",
    "description": "The representatives of the Nano network. The monitor reads them once per minute. Each IP can send 30 requests per minute, up to 10 of them at once."
  },
  "servers": [{ "url": "/" }],
  "paths": {
    "/api/v1/representatives": {
      "get": {
        "operationId": "listRepresentatives",
        "summary": "All representatives with 1,000 XNO or more, by voting weight from high to low",
        "parameters": [
          {
            "name": "show",
            "in": "query",
            "description": "Keeps only the `online` or the `principal` representatives.",
            "schema": { "type": "string", "enum": ["online", "principal"] }
          },
          {
            "name": "verdict",
            "in": "query",
            "description": "Keeps only the representatives with this verdict: `good`, `couldBeBetter` or `offline`.",
            "schema": { "$ref": "#/components/schemas/Verdict" }
          }
        ],
        "responses": {
          "200": {
            "description": "The representatives",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/RepresentativeList" } }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/representatives/{address}": {
      "get": {
        "operationId": "getRepresentative",
        "summary": "One representative with its voting weight, rank, uptime and node health per UTC day, oldest first",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "required": true,
            "description": "A `nano_` or `xrb_` address.",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "The representative",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/RepresentativeDetails" } }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "responses": {
      "Error": {
        "description": "`{\"error\": \"…\"}` with status `400` for a wrong request, `404` for an account without voting weight, and `503` until the monitor has read the representatives.",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      }
    },
    "schemas": {
      "RepresentativeList": {
        "type": "object",
        "required": ["takenAt", "latestRelease", "onlineStakeRaw", "quorumRaw", "nakamotoCoefficient", "representatives"],
        "properties": {
          "takenAt": { "$ref": "#/components/schemas/TakenAt" },
          "latestRelease": { "$ref": "#/components/schemas/LatestRelease" },
          "onlineStakeRaw": { "$ref": "#/components/schemas/OnlineStakeRaw" },
          "quorumRaw": { "$ref": "#/components/schemas/QuorumRaw" },
          "nakamotoCoefficient": { "$ref": "#/components/schemas/NakamotoCoefficient" },
          "representatives": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Representative" }
          }
        }
      },
      "RepresentativeDetails": {
        "type": "object",
        "required": ["takenAt", "latestRelease", "onlineStakeRaw", "quorumRaw", "nakamotoCoefficient", "representative", "history"],
        "properties": {
          "takenAt": { "$ref": "#/components/schemas/TakenAt" },
          "latestRelease": { "$ref": "#/components/schemas/LatestRelease" },
          "onlineStakeRaw": { "$ref": "#/components/schemas/OnlineStakeRaw" },
          "quorumRaw": { "$ref": "#/components/schemas/QuorumRaw" },
          "nakamotoCoefficient": { "$ref": "#/components/schemas/NakamotoCoefficient" },
          "representative": { "$ref": "#/components/schemas/Representative" },
          "history": {
            "type": "array",
            "description": "Oldest first.",
            "items": { "$ref": "#/components/schemas/Day" }
          }
        }
      },
      "TakenAt": { "type": "integer", "description": "The time of the reading in Unix seconds." },
      "LatestRelease": {
        "type": ["string", "null"],
        "description": "The latest nano-node release that is out for 4 weeks or longer, such as `28.2`. `null` until the monitor has read the releases.",
        "examples": ["28.2"]
      },
      "OnlineStakeRaw": { "type": "string", "description": "The online voting weight of the network in raw, as a string." },
      "QuorumRaw": { "type": "string", "description": "The online voting weight in raw that confirms a block, as a string." },
      "NakamotoCoefficient": {
        "type": ["integer", "null"],
        "description": "The fewest online representatives that together reach the quorum. `null` while the online representatives do not reach it."
      },
      "Representative": {
        "type": "object",
        "required": ["account", "name", "rank", "weightRaw", "weight", "share", "online", "principal", "node", "verdict", "outdated", "crowded"],
        "properties": {
          "account": { "type": "string", "description": "The address of the representative." },
          "name": { "type": ["string", "null"], "description": "Its name from a list of known accounts, or `null`." },
          "rank": { "type": "integer", "minimum": 1, "description": "Its rank by voting weight among all representatives, from 1." },
          "weightRaw": { "type": "string", "description": "Its voting weight in raw, as a string." },
          "weight": { "type": "number", "description": "Its voting weight in XNO." },
          "share": { "type": "number", "minimum": 0, "maximum": 1, "description": "Its share of the online voting weight, from 0 to 1. `0.0225` is 2.25%." },
          "online": { "type": "boolean", "description": "The node of the monitor has seen votes from it recently." },
          "principal": { "type": "boolean", "description": "It is online and holds at least 0.1% of the online voting weight." },
          "node": {
            "description": "Implementation (`nano-node` or `rsnano`), version, cemented blocks, uptime and sync state of its node, from telemetry. `null` if the node of the monitor is not connected to it.",
            "oneOf": [{ "$ref": "#/components/schemas/Node" }, { "type": "null" }]
          },
          "verdict": { "$ref": "#/components/schemas/Verdict" },
          "outdated": { "type": ["boolean", "null"], "description": "Its node runs a nano-node version older than `latestRelease`. `null` if the monitor cannot compare: `node` or `latestRelease` is `null`, or the node runs rsnano." },
          "crowded": { "type": "boolean", "description": "It holds 3% or more of the online voting weight." }
        }
      },
      "Node": {
        "type": "object",
        "required": ["implementation", "version", "cementedBlocks", "uptimeSeconds", "inSync"],
        "properties": {
          "implementation": { "type": "string", "enum": ["nano-node", "rsnano"] },
          "version": { "type": "string", "examples": ["28.2", "28.2.1"] },
          "cementedBlocks": { "type": "integer" },
          "uptimeSeconds": { "type": "integer" },
          "inSync": { "type": "boolean", "description": "At most 1,000 cemented blocks behind the most advanced principal representative." }
        }
      },
      "Verdict": {
        "type": "string",
        "enum": ["good", "couldBeBetter", "offline"],
        "description": "Whether accounts should delegate to it, the same check as on the account page. `good` if it is principal, not outdated and not crowded. `offline` if it is offline. `couldBeBetter` otherwise. It does not check the health of its node, such as `inSync`. `outdated: null` counts as not outdated."
      },
      "Day": {
        "type": "object",
        "required": ["date", "weight", "rank", "uptime", "polls", "inSyncRate", "telemetryPolls", "implementation", "version"],
        "properties": {
          "date": { "type": "string", "format": "date", "description": "The UTC day." },
          "weight": { "type": "integer", "description": "Its voting weight in whole XNO." },
          "rank": { "type": "integer", "minimum": 1, "description": "Its rank by voting weight on this day, from 1." },
          "uptime": {
            "type": ["number", "null"],
            "minimum": 0,
            "maximum": 1,
            "description": "The share of the polls of the day that found it `online`, from 0 to 1. `null` if `polls` is 0."
          },
          "polls": {
            "type": "integer",
            "minimum": 0,
            "description": "How often the monitor read the representatives on this day: once per minute, but not while its node is syncing. Today has the polls so far. 0 for the days before the monitor counted them."
          },
          "inSyncRate": {
            "type": ["number", "null"],
            "minimum": 0,
            "maximum": 1,
            "description": "The share of the `telemetryPolls` that found its node in sync, as in `node.inSync`, from 0 to 1. `null` if `telemetryPolls` is 0 or `null`."
          },
          "telemetryPolls": {
            "type": ["integer", "null"],
            "minimum": 0,
            "description": "How many of the `polls` had telemetry of its node. The monitor only gets telemetry from the nodes that its node is connected to, mostly principal representatives. `null` for the days before the monitor counted them."
          },
          "implementation": {
            "type": ["string", "null"],
            "enum": ["nano-node", "rsnano", null],
            "description": "The implementation of its node at the last poll of the day with telemetry. `null` if no poll of the day had telemetry."
          },
          "version": {
            "type": ["string", "null"],
            "description": "The version of its node at the last poll of the day with telemetry, such as `28.2`. `null` if no poll of the day had telemetry."
          }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": { "error": { "type": "string" } }
      }
    }
  }
}
