{
  "openapi": "3.1.0",
  "info": {
    "title": "PasswordRadar API",
    "version": "0.1.0",
    "description": "Password screening by k-anonymity: send six characters of a salted hash, compare the returned hashes locally. The raw password and its full hash never leave your server. Guide: https://passwordradar.eu/docs.html",
    "contact": {
      "name": "PasswordRadar",
      "email": "hello@passwordradar.eu",
      "url": "https://passwordradar.eu/"
    }
  },
  "servers": [
    {
      "url": "https://api.knownpass.com",
      "description": "Production. The API host keeps the project's original name."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/v1/range/{prefix}": {
      "get": {
        "description": "Hash the password locally as the lowercase hex SHA-1 of the salt `Salted for knownpass.com: ` followed by the password. Send only the first six characters. The response maps every known full hash in that bucket to the categories it was found in; check locally whether your full hash is one of the keys. The salt names the project's original domain (PasswordRadar was called knownPass) and is part of every hash in the dataset, so it never changes.",
        "operationId": "range_lookup_v1_range__prefix__get",
        "parameters": [
          {
            "in": "path",
            "name": "prefix",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{6}$",
              "type": "string"
            },
            "description": "The first six characters of the salted SHA-1, lowercase hex."
          }
        ],
        "responses": {
          "200": {
            "description": "Known hashes in this bucket, each with its categories. An empty `results` object means nothing is known in this bucket.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RangeResponse"
                }
              }
            }
          },
          "400": {
            "description": "The prefix is not exactly six lowercase hex characters (`invalid_input`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`unauthorized`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Over the daily allowance without billing or prepaid credit. `Retry-After` gives the wait in seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "summary": "Look up a hash prefix"
      }
    },
    "/v1/health": {
      "get": {
        "operationId": "health_v1_health_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "title": "Response Health V1 Health Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Service health",
        "description": "Unauthenticated liveness check. Answers `{\"status\": \"ok\"}`.",
        "security": []
      }
    },
    "/v1/bulk/{dataset}/manifest": {
      "get": {
        "description": "Lists the published files of a dataset for self-hosting. Needs a key with the bulk-download entitlement (every plan, Free included).",
        "operationId": "bulk_manifest_v1_bulk__dataset__manifest_get",
        "parameters": [
          {
            "in": "path",
            "name": "dataset",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The dataset's published manifest.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`unauthorized`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key has no bulk-download entitlement for this dataset (`forbidden`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "summary": "Bulk download: dataset manifest"
      }
    },
    "/v1/bulk/{dataset}/files/{filename}": {
      "get": {
        "description": "Serves one file listed in the manifest. Supports `Range` requests, so an interrupted download can resume.",
        "operationId": "bulk_file_v1_bulk__dataset__files__filename__get",
        "parameters": [
          {
            "in": "path",
            "name": "dataset",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "filename",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "The requested byte range of the file.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key (`unauthorized`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key has no bulk-download entitlement for this dataset (`forbidden`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "summary": "Bulk download: one file"
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key: `Authorization: Bearer kpp_…`"
      }
    },
    "schemas": {
      "RangeResponse": {
        "type": "object",
        "required": [
          "prefix",
          "results"
        ],
        "properties": {
          "prefix": {
            "type": "string",
            "pattern": "^[0-9a-f]{6}$",
            "description": "The prefix you sent."
          },
          "results": {
            "type": "object",
            "description": "Full 40-character lowercase hash → categories it was found in.",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          },
          "request_id": {
            "type": "string"
          }
        }
      }
    }
  }
}
