{
  "openapi": "3.1.0",
  "info": {
    "title": "PassportTax Partner Queries",
    "version": "2.0.0",
    "description": "Structured passport queries for the same person's passports. Legacy API v1 scores retain their existing semantics. Query results carry actual source universe, exclusions, status criterion, observation date and immutable release identity. The default criterion is VF + VOA + ETA; eVisa is not included."
  },
  "servers": [{ "url": "https://passporttax.org", "description": "Configured production origin; availability, active release and operating limits require separate hosted acceptance. This URL is not deployment evidence." }],
  "security": [],
  "paths": {
    "/api/query": {
      "get": {
        "operationId": "queryPassports",
        "summary": "Query passports using URL parameters",
        "description": "Maximum URL size is 4096 bytes. At most ten normalized unique passports across all roles. Production query quotas and latency guarantees are not yet agreed; an absent rate limiter is not unlimited service.",
        "parameters": [
          { "name": "operation", "in": "query", "required": true, "schema": { "type": "string", "enum": ["list", "destination", "union", "intersection", "upgrade"] } },
          { "name": "passports", "in": "query", "style": "form", "explode": false, "schema": { "type": "array", "items": { "type": "string" } }, "example": ["DM", "TR"] },
          { "name": "base", "in": "query", "style": "form", "explode": false, "schema": { "type": "array", "items": { "type": "string" } } },
          { "name": "candidates", "in": "query", "style": "form", "explode": false, "schema": { "type": "array", "items": { "type": "string" } } },
          { "name": "destination", "in": "query", "schema": { "type": "string" }, "example": "JP" },
          { "name": "statuses", "in": "query", "style": "form", "explode": false, "schema": { "type": "array", "items": { "type": "string", "enum": ["VF", "VOA", "ETA", "EV", "VR", "NA"] } } },
          { "name": "locale", "in": "query", "schema": { "type": "string", "enum": ["zh-Hant", "zh-Hans", "en"] } },
          { "name": "edition", "in": "query", "schema": { "type": "string", "default": "latest-published" }, "description": "latest-published or an explicit r-<content hash> release ID." }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/QueryResult" },
          "400": { "$ref": "#/components/responses/QueryError" },
          "404": { "$ref": "#/components/responses/QueryError" },
          "410": { "$ref": "#/components/responses/QueryError" },
          "414": { "$ref": "#/components/responses/QueryError" },
          "503": { "$ref": "#/components/responses/QueryError" },
          "502": { "$ref": "#/components/responses/QueryError" }
        }
      },
      "post": {
        "operationId": "queryPassportsJson",
        "summary": "Query passports using a JSON body",
        "description": "Maximum JSON body size is 16384 bytes; request-body reading defaults to a 10-second deadline. This is not a total response-time SLA. At most ten normalized unique passports across all roles. Production query quotas and capacity remain subject to hosted acceptance.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "schemas/query.schema.json#/$defs/request" },
              "example": { "operation": "destination", "passports": ["DM", "TR"], "destination": "JP", "locale": "zh-Hant", "edition": "latest-published" }
            }
          }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/QueryResult" },
          "400": { "$ref": "#/components/responses/QueryError" },
          "404": { "$ref": "#/components/responses/QueryError" },
          "408": { "$ref": "#/components/responses/QueryError" },
          "410": { "$ref": "#/components/responses/QueryError" },
          "413": { "$ref": "#/components/responses/QueryError" },
          "415": { "$ref": "#/components/responses/QueryError" },
          "503": { "$ref": "#/components/responses/QueryError" },
          "502": { "$ref": "#/components/responses/QueryError" }
        }
      }
    },
    "/api/v2/latest.json": {
      "get": {
        "operationId": "getLatestPublishedRelease",
        "summary": "Resolve the current published release once per query",
        "responses": { "200": { "description": "Mutable pointer; revalidate before use.", "content": { "application/json": { "schema": { "type": "object", "required": ["schema_version", "release_id", "manifest_path", "manifest_sha256"] } } } } }
      }
    },
    "/api/v2/releases.json": {
      "get": {
        "operationId": "getReleaseLifecycle",
        "summary": "Check publication and withdrawal state",
        "responses": { "200": { "description": "Lifecycle status is separate from immutable release bytes.", "content": { "application/json": { "schema": { "type": "object", "required": ["schema_version", "releases"] } } } } }
      }
    }
  },
  "components": {
    "responses": {
      "QueryResult": {
        "description": "Complete evaluated-domain rows with matched and unresolved destination sets. Counts are not aliases for legacy headline or audit scores.",
        "content": { "application/json": { "schema": { "$ref": "schemas/query.schema.json#/$defs/response" } } }
      },
      "QueryError": {
        "description": "Invalid input, unavailable data, withdrawn release or integrity failure. Errors do not fabricate a zero result.",
        "content": { "application/json": { "schema": { "type": "object", "required": ["error"], "properties": { "error": { "type": "object", "required": ["code", "message"], "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} } } } } } }
      }
    }
  }
}
