{
  "openapi": "3.1.0",
  "info": {
    "title": "Roadwork Data API",
    "version": "0.2.0",
    "description": "Authenticated WZDx 4.2 and CWZ-compatible 1.0 exports, plus an internal normalized representation. CWZ-compatible FeedInfo changes the license enumeration to service terms; all other schema rules remain validated. It intentionally does not conform to the original CC0-only license enumeration. Research exports include available sources regardless of declared license; commercial approval remains a separate source filter. Public samples are synthetic. Customer keys require account activation and enforce shared account quotas. Billing is disabled."
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "security": [
    {
      "ApiKey": []
    },
    {
      "Bearer": []
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      },
      "Bearer": {
        "type": "http",
        "scheme": "bearer"
      },
      "Session": {
        "type": "apiKey",
        "in": "cookie",
        "name": "wz_session"
      }
    }
  },
  "paths": {
    "/v1/work-zones": {
      "get": {
        "summary": "Filtered work zones; follow Link rel=next",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "feed_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "bbox",
            "in": "query",
            "schema": {
              "type": "string",
              "description": "west,south,east,north"
            }
          },
          {
            "name": "after",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_missing",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "extensions",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "normalized",
                "wzdx",
                "cwz"
              ],
              "default": "normalized"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 1000000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Quota-Limit": {
                "description": "Customer monthly request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Quota-Remaining": {
                "description": "Customer monthly requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Limit": {
                "description": "Customer monthly complete-export request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Remaining": {
                "description": "Customer complete-export requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "400": {
            "description": "Invalid query"
          },
          "401": {
            "description": "Invalid or missing API key"
          },
          "500": {
            "description": "Database or export validation error"
          },
          "403": {
            "description": "Pilot activation or entitlement required"
          },
          "429": {
            "description": "Per-minute or monthly quota reached; no automatic overages"
          }
        },
        "description": "Freshness evidence is in normalized feature properties or, when extensions=true, each WZDx/CWZ feature x_work_zone_api: feed_last_success (successful fetch or 304), source_updated (nullable publisher timestamp), freshness_threshold_seconds, feed_stale, source_timestamp_stale, first_seen, last_seen and missing_since. A false source_timestamp_stale with null source_updated means unknown freshness. Absence alone means service status unknown, not canceled/completed. Outages do not infer absence. These fields describe observations, not confirmed on-road activity."
      }
    },
    "/v1/feeds/{format}": {
      "get": {
        "summary": "Complete filtered WZDx 4.2 or CWZ 1.0 feed; no pagination",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "feed_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "bbox",
            "in": "query",
            "schema": {
              "type": "string",
              "description": "west,south,east,north"
            }
          },
          {
            "name": "after",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_missing",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "extensions",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "format",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "wzdx",
                "cwz"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Quota-Limit": {
                "description": "Customer monthly request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Quota-Remaining": {
                "description": "Customer monthly requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Limit": {
                "description": "Customer monthly complete-export request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Remaining": {
                "description": "Customer complete-export requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "400": {
            "description": "Invalid query"
          },
          "401": {
            "description": "Invalid or missing API key"
          },
          "500": {
            "description": "Database or export validation error"
          },
          "403": {
            "description": "Pilot activation or entitlement required"
          },
          "429": {
            "description": "Per-minute, monthly request or monthly export allowance exhausted; no overages."
          }
        },
        "description": "Pilot default: 30 complete-export request admissions per UTC month, shared across all keys, formats and filters and within the normal request allowance. Admitted errors consume both allowances. An exhausted export allowance does not block paginated queries when normal allowance remains. Freshness evidence is in normalized feature properties or, when extensions=true, each WZDx/CWZ feature x_work_zone_api: feed_last_success (successful fetch or 304), source_updated (nullable publisher timestamp), freshness_threshold_seconds, feed_stale, source_timestamp_stale, first_seen, last_seen and missing_since. A false source_timestamp_stale with null source_updated means unknown freshness. Absence alone means service status unknown, not canceled/completed. Outages do not infer absence. These fields describe observations, not confirmed on-road activity."
      }
    },
    "/v1/work-zones/{id}": {
      "get": {
        "summary": "Latest observation in requested format",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "normalized",
                "wzdx",
                "cwz"
              ],
              "default": "normalized"
            }
          },
          {
            "name": "extensions",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Quota-Limit": {
                "description": "Customer monthly request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Quota-Remaining": {
                "description": "Customer monthly requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Limit": {
                "description": "Customer monthly complete-export request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Remaining": {
                "description": "Customer complete-export requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "400": {
            "description": "Invalid query"
          },
          "401": {
            "description": "Invalid or missing API key"
          },
          "500": {
            "description": "Database or export validation error"
          },
          "404": {
            "description": "Missing or unavailable event"
          },
          "422": {
            "description": "Observation cannot be represented in requested standard; normalized representation remains available"
          },
          "403": {
            "description": "Pilot activation or entitlement required"
          },
          "429": {
            "description": "Per-minute or monthly quota reached; no automatic overages"
          }
        },
        "description": "Freshness evidence is in normalized feature properties or, when extensions=true, each WZDx/CWZ feature x_work_zone_api: feed_last_success (successful fetch or 304), source_updated (nullable publisher timestamp), freshness_threshold_seconds, feed_stale, source_timestamp_stale, first_seen, last_seen and missing_since. A false source_timestamp_stale with null source_updated means unknown freshness. Absence alone means service status unknown, not canceled/completed. Outages do not infer absence. These fields describe observations, not confirmed on-road activity."
      }
    },
    "/v1/work-zones/{id}/history": {
      "get": {
        "summary": "Latest 100 normalized revisions",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Quota-Limit": {
                "description": "Customer monthly request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Quota-Remaining": {
                "description": "Customer monthly requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Limit": {
                "description": "Customer monthly complete-export request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Remaining": {
                "description": "Customer complete-export requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "event_id",
                    "revisions",
                    "limit",
                    "available_revision_count",
                    "earliest_stored_revision",
                    "latest_stored_revision",
                    "retained_history_complete",
                    "page_truncated"
                  ],
                  "properties": {
                    "event_id": {
                      "type": "string"
                    },
                    "revisions": {
                      "type": "array",
                      "maxItems": 100,
                      "items": {
                        "type": "object"
                      }
                    },
                    "limit": {
                      "const": 100
                    },
                    "available_revision_count": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "earliest_stored_revision": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "latest_stored_revision": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "retained_history_complete": {
                      "type": "boolean",
                      "description": "Every numbered revision from 1 through latest_stored_revision is stored; not a promise of all publisher changes."
                    },
                    "page_truncated": {
                      "type": "boolean",
                      "description": "More revisions are stored than returned on this page."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query"
          },
          "401": {
            "description": "Invalid or missing API key"
          },
          "500": {
            "description": "Database or export validation error"
          },
          "403": {
            "description": "Pilot activation or entitlement required"
          },
          "429": {
            "description": "Per-minute or monthly quota reached; no automatic overages"
          }
        },
        "description": "Up to 100 normalized semantic revisions, newest first. Stored-history metadata distinguishes missing numbered history from a response-page limit. History is not a raw publisher snapshot archive or a factual-accuracy guarantee."
      }
    },
    "/v1/sources": {
      "get": {
        "summary": "Source health, declared licenses and access requirements",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Quota-Limit": {
                "description": "Customer monthly request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Quota-Remaining": {
                "description": "Customer monthly requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Limit": {
                "description": "Customer monthly complete-export request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Remaining": {
                "description": "Customer complete-export requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "400": {
            "description": "Invalid query"
          },
          "401": {
            "description": "Invalid or missing API key"
          },
          "500": {
            "description": "Database or export validation error"
          },
          "403": {
            "description": "Pilot activation or entitlement required"
          },
          "429": {
            "description": "Per-minute or monthly quota reached; no automatic overages"
          }
        }
      }
    },
    "/v1/notices": {
      "get": {
        "summary": "Required publisher notices",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "headers": {
              "X-Quota-Limit": {
                "description": "Customer monthly request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Quota-Remaining": {
                "description": "Customer monthly requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Limit": {
                "description": "Customer monthly complete-export request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Remaining": {
                "description": "Customer complete-export requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "400": {
            "description": "Invalid query"
          },
          "401": {
            "description": "Invalid or missing API key"
          },
          "500": {
            "description": "Database or export validation error"
          },
          "403": {
            "description": "Pilot activation or entitlement required"
          },
          "429": {
            "description": "Per-minute or monthly quota reached; no automatic overages"
          }
        }
      }
    },
    "/healthz": {
      "get": {
        "summary": "Process liveness",
        "security": [],
        "responses": {
          "200": {
            "description": "Available"
          },
          "503": {
            "description": "Unavailable"
          }
        }
      }
    },
    "/readyz": {
      "get": {
        "summary": "Database readiness (not upstream freshness)",
        "security": [],
        "responses": {
          "200": {
            "description": "Available"
          },
          "503": {
            "description": "Unavailable"
          }
        }
      }
    },
    "/notices": {
      "get": {
        "summary": "Required CDOT notice",
        "security": [],
        "responses": {
          "200": {
            "description": "Available"
          },
          "503": {
            "description": "Unavailable"
          }
        }
      }
    },
    "/api/sample": {
      "get": {
        "summary": "Twelve authored synthetic observations; not actual work zones",
        "security": [],
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "wzdx",
                "cwz",
                "normalized"
              ],
              "default": "normalized"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Synthetic sample with X-Sample-Data: synthetic"
          }
        }
      }
    },
    "/api/account/signup": {
      "post": {
        "summary": "Create pending evaluation account; no payment collected",
        "security": [],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "201": {
            "description": "Created"
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "description": "Sign in required"
          },
          "403": {
            "description": "Same-origin mutation required"
          },
          "429": {
            "description": "Action limit reached"
          }
        },
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must match the configured public origin or local request origin."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "email",
                  "password",
                  "organization",
                  "accept_terms"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "password": {
                    "type": "string",
                    "minLength": 12,
                    "maxLength": 256
                  },
                  "organization": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 160
                  },
                  "accept_terms": {
                    "const": true
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/account/login": {
      "post": {
        "summary": "Create an HttpOnly session",
        "security": [],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "201": {
            "description": "Created"
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "description": "Sign in required"
          },
          "403": {
            "description": "Same-origin mutation required"
          },
          "429": {
            "description": "Action limit reached"
          }
        },
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must match the configured public origin or local request origin."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "password": {
                    "type": "string",
                    "minLength": 12,
                    "maxLength": 256
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/account/logout": {
      "post": {
        "summary": "Revoke the current session",
        "security": [
          {
            "Session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "201": {
            "description": "Created"
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "description": "Sign in required"
          },
          "403": {
            "description": "Same-origin mutation required"
          },
          "429": {
            "description": "Action limit reached"
          }
        },
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must match the configured public origin or local request origin."
          }
        ]
      }
    },
    "/api/account": {
      "get": {
        "summary": "Account settings, key prefixes and aggregate usage",
        "security": [
          {
            "Session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "201": {
            "description": "Created"
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "description": "Sign in required"
          },
          "403": {
            "description": "Same-origin mutation required"
          },
          "429": {
            "description": "Action limit reached"
          }
        }
      }
    },
    "/api/account/keys": {
      "post": {
        "summary": "Create a key; raw value returned once",
        "security": [
          {
            "Session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "201": {
            "description": "Created"
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "description": "Sign in required"
          },
          "403": {
            "description": "Same-origin mutation required"
          },
          "429": {
            "description": "Action limit reached"
          }
        },
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must match the configured public origin or local request origin."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "label"
                ],
                "properties": {
                  "label": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/account/keys/{id}": {
      "delete": {
        "summary": "Revoke an owned key immediately",
        "security": [
          {
            "Session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "201": {
            "description": "Created"
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "description": "Sign in required"
          },
          "403": {
            "description": "Same-origin mutation required"
          },
          "429": {
            "description": "Action limit reached"
          }
        },
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must match the configured public origin or local request origin."
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/account/keys/{id}/rotate": {
      "post": {
        "summary": "Atomically revoke an owned key and issue its replacement",
        "security": [
          {
            "Session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "201": {
            "description": "Created"
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "description": "Sign in required"
          },
          "403": {
            "description": "Same-origin mutation required"
          },
          "429": {
            "description": "Action limit reached"
          }
        },
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must match the configured public origin or local request origin."
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "label"
                ],
                "properties": {
                  "label": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/account/pilot-request": {
      "post": {
        "summary": "Save a request for operator review; no external message is sent",
        "security": [
          {
            "Session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response"
          },
          "201": {
            "description": "Created"
          },
          "400": {
            "description": "Invalid request"
          },
          "401": {
            "description": "Sign in required"
          },
          "403": {
            "description": "Same-origin mutation required"
          },
          "429": {
            "description": "Action limit reached"
          }
        },
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must match the configured public origin or local request origin."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "use_case"
                ],
                "properties": {
                  "use_case": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 2000
                  },
                  "regions": {
                    "type": "string",
                    "maxLength": 300
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/quality": {
      "get": {
        "summary": "Measure stored source completeness and observed identity decisions",
        "description": "Inventory includes present stored observations, including ended records; historical missing records are separate. Identity counters cover successful parsed response bodies only, exclude initial baselines, and do not measure ground-truth correctness or uptime. Telemetry begins with this version; no historical backfill. Normal account quotas and commercial-source filtering apply.",
        "parameters": [
          {
            "name": "feed_id",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "One enabled, permitted source; otherwise all permitted sources."
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 30,
              "default": 7
            },
            "description": "Longitudinal telemetry window; inventory describes current stored state."
          }
        ],
        "responses": {
          "200": {
            "description": "Per-feed inventory and identity decision counts, freshness and explicit limitations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "feeds",
                    "limitations",
                    "inventory_scope",
                    "generated_at"
                  ],
                  "properties": {
                    "feeds": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "limitations": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "inventory_scope": {
                      "type": "string"
                    },
                    "generated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            },
            "headers": {
              "X-Quota-Limit": {
                "description": "Customer monthly request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Quota-Remaining": {
                "description": "Customer monthly requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Limit": {
                "description": "Customer monthly complete-export request allowance. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-Export-Quota-Remaining": {
                "description": "Customer complete-export requests remaining after admission. Operator keys bypass customer quotas and do not receive this header.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "400": {
            "description": "Invalid days window"
          },
          "401": {
            "description": "Invalid API key"
          },
          "403": {
            "description": "Account activation required"
          },
          "404": {
            "description": "Source unavailable"
          },
          "429": {
            "description": "Account quota exceeded"
          },
          "503": {
            "description": "Report already running or database unavailable",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds before retry when another report is running"
              }
            }
          }
        }
      }
    },
    "/api/account/password": {
      "post": {
        "summary": "Change password and sign out all browser sessions",
        "description": "Requires current password. Invalidates outstanding recovery links and all browser sessions. API-key revocation is optional. Sign in again normally.",
        "security": [
          {
            "Session": []
          }
        ],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must match the configured public origin or local request origin."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "password",
                  "confirm_password",
                  "current_password"
                ],
                "properties": {
                  "password": {
                    "type": "string",
                    "format": "password",
                    "writeOnly": true,
                    "minLength": 1,
                    "maxLength": 256,
                    "description": "Server requires 12–256 UTF-8 bytes; character counts alone do not express this byte limit."
                  },
                  "confirm_password": {
                    "type": "string",
                    "format": "password",
                    "writeOnly": true,
                    "minLength": 1,
                    "maxLength": 256,
                    "description": "Must match password exactly; same UTF-8 byte limits apply."
                  },
                  "current_password": {
                    "type": "string",
                    "format": "password",
                    "writeOnly": true,
                    "minLength": 1,
                    "maxLength": 256
                  },
                  "revoke_api_keys": {
                    "type": "boolean",
                    "default": false,
                    "description": "Also revoke all API keys. All browser sessions are always invalidated."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password changed; sign in required",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "password_changed",
                    "sign_in_required",
                    "api_keys_revoked"
                  ],
                  "properties": {
                    "password_changed": {
                      "const": true
                    },
                    "sign_in_required": {
                      "const": true
                    },
                    "api_keys_revoked": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid or mismatched password, or invalid/expired/used recovery link"
          },
          "403": {
            "description": "Same-origin mutation required"
          },
          "415": {
            "description": "JSON content type required"
          },
          "429": {
            "description": "Authentication or portal action limit reached"
          },
          "503": {
            "description": "Authentication busy or storage unavailable"
          },
          "401": {
            "description": "Session expired, current password incorrect or a concurrent password change occurred"
          }
        }
      }
    },
    "/api/account/recover": {
      "post": {
        "summary": "Reset password using an operator-issued link",
        "description": "Recovery revokes all API keys and browser sessions without changing account/source entitlements. No automatic email delivery or sign-in.",
        "security": [],
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must match the configured public origin or local request origin."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "password",
                  "confirm_password",
                  "token"
                ],
                "properties": {
                  "password": {
                    "type": "string",
                    "format": "password",
                    "writeOnly": true,
                    "minLength": 1,
                    "maxLength": 256,
                    "description": "Server requires 12–256 UTF-8 bytes; character counts alone do not express this byte limit."
                  },
                  "confirm_password": {
                    "type": "string",
                    "format": "password",
                    "writeOnly": true,
                    "minLength": 1,
                    "maxLength": 256,
                    "description": "Must match password exactly; same UTF-8 byte limits apply."
                  },
                  "token": {
                    "type": "string",
                    "writeOnly": true,
                    "minLength": 52,
                    "maxLength": 52,
                    "description": "Private single-use operator-issued recovery token, valid for 15 minutes. Never put it in request URLs or logs."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Password changed; sign in required",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "password_changed",
                    "sign_in_required",
                    "api_keys_revoked"
                  ],
                  "properties": {
                    "password_changed": {
                      "const": true
                    },
                    "sign_in_required": {
                      "const": true
                    },
                    "api_keys_revoked": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid or mismatched password, or invalid/expired/used recovery link"
          },
          "403": {
            "description": "Same-origin mutation required"
          },
          "415": {
            "description": "JSON content type required"
          },
          "429": {
            "description": "Authentication or portal action limit reached"
          },
          "503": {
            "description": "Authentication busy or storage unavailable"
          }
        }
      }
    },
    "/v1/coverage": {
      "get": {
        "summary": "Current observation coverage and real examples",
        "description": "Authenticated inventory of enabled permitted sources, availability, present observation counts, 0.5 degree cells based on source-bounds centres and up to two original-geometry examples per source. Missing and ended/completed/canceled observations are excluded. Counts are not deduplicated. Cached up to 60 seconds; source eligibility changes invalidate the cache. This is not geographic completeness or verified closure evidence.",
        "security": [
          {
            "ApiKey": []
          },
          {
            "Bearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "workzone-coverage-v1 JSON inventory"
          },
          "401": {
            "description": "Invalid API key"
          },
          "403": {
            "description": "Access awaits activation or data mode approval"
          },
          "429": {
            "description": "Account allowance exceeded"
          },
          "503": {
            "description": "Coverage inventory unavailable"
          }
        }
      }
    },
    "/api/coverage-snapshot": {
      "get": {
        "summary": "Historical coverage snapshot for the sample map",
        "description": "Frozen operator-selected historical observations. Commercial instances filter every source by current reviewed permissions. This route does not query live inventory.",
        "security": [],
        "responses": {
          "200": {
            "description": "Historical coverage inventory with recorded timestamp, cell counts, selected publisher examples and notices"
          },
          "503": {
            "description": "No historical snapshot is configured or source permissions are unavailable"
          }
        }
      }
    }
  }
}
