{
  "openapi": "3.1.0",
  "info": {
    "title": "Digital Democracy API — legacy",
    "version": "legacy",
    "description": "Read-only state legislative data. Every endpoint is a `GET` returning `{data}`, plus pagination keys on the paginated ones. The HTTP status line carries the status; errors return `{msg}`.\n\nThe version is the first path segment and the jurisdiction the second, and both are required — `/legacy/ca/bill/X`. There is no unversioned alias and no default jurisdiction; a URL missing either segment returns 404.\n\nGENERATED FILE. Produced by `npm run docs:build` from the route table, so it cannot list an endpoint that does not exist. Field types come from the route serializers; a field whose source column is absent is OMITTED from the response rather than returned as null.\n\nVersion status: current."
  },
  "servers": [
    {
      "url": "https://api.digitaldemocracy.org",
      "description": "production"
    },
    {
      "url": "https://preprod-api.digitaldemocracy.org",
      "description": "preprod"
    },
    {
      "url": "https://develop-api.digitaldemocracy.org",
      "description": "develop"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Entity",
      "description": "One composed object, addressed by id."
    },
    {
      "name": "Directory",
      "description": "Paginated listings."
    },
    {
      "name": "Feeds",
      "description": "Paginated `elasticsearch/*` feeds for an indexer."
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Required on every endpoint. Grants are matched on resource x method x version x jurisdiction; `GET /whoami` reports what your key holds."
      }
    }
  },
  "paths": {
    "/legacy/{state}/elasticsearch/utterances": {
      "get": {
        "operationId": "elasticsearchUtterances",
        "summary": "/elasticsearch/utterances",
        "tags": [
          "Feeds"
        ],
        "description": "Requires a grant for resource `elasticsearch.utterances` at this version and jurisdiction.\n\nOne of the seven `elasticsearch/*` feeds. **These run no Elasticsearch** — the name describes the consumer. They are paginated SQL feeds meant to be walked incrementally by a search indexer, and they do not accept `session_year`.",
        "x-required-grant": "elasticsearch.utterances",
        "x-route-family": "feed",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "An unparseable value falls back to the default rather than erroring. `page=0` produces a negative offset and emits a bare LIMIT.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Capped at 1000 for most endpoints, 5000 for `elasticsearch/utterances` and `tipsheets/recent`, and 15000 for `elasticsearch/financials`. A decimal value falls back to the default rather than truncating.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "start_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "end_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally. **`end_date` on `elasticsearch/organizations` returns a 500** — a known defect, reproduced deliberately. Use `start_date`.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "hid[]",
            "in": "query",
            "required": false,
            "description": "Restrict to utterances from these hearings.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              }
            }
          },
          {
            "name": "uid[]",
            "in": "query",
            "required": false,
            "description": "Restrict to these utterance ids.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              }
            }
          },
          {
            "name": "did[]",
            "in": "query",
            "required": false,
            "description": "Restrict to these discussion ids.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "total_results": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "utterances": {
                          "type": "array",
                          "description": "Transcribed hearing testimony, newest first. Each entry also carries the speaker fields merged in from a second query.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "did": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "uid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "content": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "hid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "finalized": {
                                "type": [
                                  "boolean",
                                  "null"
                                ]
                              },
                              "file_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "timestamp": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "last_modified": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "date": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "pid": {
                                "type": [
                                  "integer",
                                  "null"
                                ],
                                "description": "Speaker id, merged in from the person query."
                              },
                              "isFake": {
                                "type": [
                                  "boolean",
                                  "null"
                                ],
                                "description": "Placeholder-person flag."
                              },
                              "first": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "Speaker first name."
                              },
                              "last": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "Speaker last name."
                              },
                              "party": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "Speaker party."
                              },
                              "person_type": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "`legislator` when the row has a current term, otherwise `person`."
                              }
                            },
                            "required": [
                              "first",
                              "isFake",
                              "last",
                              "party",
                              "person_type",
                              "pid"
                            ]
                          }
                        }
                      },
                      "required": [
                        "utterances"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/elasticsearch/organizations": {
      "get": {
        "operationId": "elasticsearchOrganizations",
        "summary": "/elasticsearch/organizations",
        "tags": [
          "Feeds"
        ],
        "description": "Requires a grant for resource `elasticsearch.organizations` at this version and jurisdiction.\n\nOne of the seven `elasticsearch/*` feeds. **These run no Elasticsearch** — the name describes the consumer. They are paginated SQL feeds meant to be walked incrementally by a search indexer, and they do not accept `session_year`.",
        "x-required-grant": "elasticsearch.organizations",
        "x-route-family": "feed",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "An unparseable value falls back to the default rather than erroring. `page=0` produces a negative offset and emits a bare LIMIT.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Capped at 1000 for most endpoints, 5000 for `elasticsearch/utterances` and `tipsheets/recent`, and 15000 for `elasticsearch/financials`. A decimal value falls back to the default rather than truncating.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "start_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "end_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally. **`end_date` on `elasticsearch/organizations` returns a 500** — a known defect, reproduced deliberately. Use `start_date`.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "oid[]",
            "in": "query",
            "required": false,
            "description": "Restrict to these organization ids.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "total_results": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "orgs": {
                          "type": "array",
                          "description": "Lobbying organizations. `aliases` has NO guaranteed order — compare it as an unordered set.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "oid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "name": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "industry": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "aliases": {},
                              "type": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "last_modified": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              }
                            },
                            "required": [
                              "aliases"
                            ]
                          }
                        }
                      },
                      "required": [
                        "orgs"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/elasticsearch/committees": {
      "get": {
        "operationId": "elasticsearchCommittees",
        "summary": "/elasticsearch/committees",
        "tags": [
          "Feeds"
        ],
        "description": "Requires a grant for resource `elasticsearch.committees` at this version and jurisdiction.\n\nOne of the seven `elasticsearch/*` feeds. **These run no Elasticsearch** — the name describes the consumer. They are paginated SQL feeds meant to be walked incrementally by a search indexer, and they do not accept `session_year`.",
        "x-required-grant": "elasticsearch.committees",
        "x-route-family": "feed",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "An unparseable value falls back to the default rather than erroring. `page=0` produces a negative offset and emits a bare LIMIT.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Capped at 1000 for most endpoints, 5000 for `elasticsearch/utterances` and `tipsheets/recent`, and 15000 for `elasticsearch/financials`. A decimal value falls back to the default rather than truncating.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "start_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "end_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally. **`end_date` on `elasticsearch/organizations` returns a 500** — a known defect, reproduced deliberately. Use `start_date`.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cid[]",
            "in": "query",
            "required": false,
            "description": "Restrict to these committee ids.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "total_results": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "committees": {
                          "type": "array",
                          "description": "Committees matching the filters.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "chamber": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "cid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "name": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "session_year": {},
                              "type": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "last_modified": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "pids": {
                                "type": "array",
                                "description": "Member person ids, parsed from a comma-joined string. `[]` when unparseable."
                              },
                              "topic": {
                                "type": "array",
                                "description": "KNOWN BUG (preserved): split on WHITESPACE rather than on the \", \" separator, so a multi-word topic is torn apart and commas are kept."
                              }
                            },
                            "required": [
                              "pids",
                              "session_year",
                              "topic"
                            ]
                          }
                        }
                      },
                      "required": [
                        "committees"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/elasticsearch/people": {
      "get": {
        "operationId": "elasticsearchPeople",
        "summary": "/elasticsearch/people",
        "tags": [
          "Feeds"
        ],
        "description": "Requires a grant for resource `elasticsearch.people` at this version and jurisdiction.\n\nOne of the seven `elasticsearch/*` feeds. **These run no Elasticsearch** — the name describes the consumer. They are paginated SQL feeds meant to be walked incrementally by a search indexer, and they do not accept `session_year`.",
        "x-required-grant": "elasticsearch.people",
        "x-route-family": "feed",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "An unparseable value falls back to the default rather than erroring. `page=0` produces a negative offset and emits a bare LIMIT.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Capped at 1000 for most endpoints, 5000 for `elasticsearch/utterances` and `tipsheets/recent`, and 15000 for `elasticsearch/financials`. A decimal value falls back to the default rather than truncating.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "start_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "end_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally. **`end_date` on `elasticsearch/organizations` returns a 500** — a known defect, reproduced deliberately. Use `start_date`.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "pid[]",
            "in": "query",
            "required": false,
            "description": "Restrict to these person ids.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "total_results": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "people": {
                          "type": "array",
                          "description": "People matching the filters.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "image": {},
                              "pid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "first": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "last": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "testimony_minutes": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "last_modified": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "type": {
                                "type": "array",
                                "description": "One single-key object per affiliation: `{<person_type>: [session years]}`."
                              }
                            },
                            "required": [
                              "image",
                              "type"
                            ]
                          }
                        }
                      },
                      "required": [
                        "people"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/elasticsearch/bills": {
      "get": {
        "operationId": "elasticsearchBills",
        "summary": "/elasticsearch/bills",
        "tags": [
          "Feeds"
        ],
        "description": "Requires a grant for resource `elasticsearch.bills` at this version and jurisdiction.\n\nOne of the seven `elasticsearch/*` feeds. **These run no Elasticsearch** — the name describes the consumer. They are paginated SQL feeds meant to be walked incrementally by a search indexer, and they do not accept `session_year`.",
        "x-required-grant": "elasticsearch.bills",
        "x-route-family": "feed",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "An unparseable value falls back to the default rather than erroring. `page=0` produces a negative offset and emits a bare LIMIT.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Capped at 1000 for most endpoints, 5000 for `elasticsearch/utterances` and `tipsheets/recent`, and 15000 for `elasticsearch/financials`. A decimal value falls back to the default rather than truncating.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "start_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "end_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally. **`end_date` on `elasticsearch/organizations` returns a 500** — a known defect, reproduced deliberately. Use `start_date`.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "bid[]",
            "in": "query",
            "required": false,
            "description": "Restrict to these bill ids. Regex-validated as `^\\w+$` — a hyphen is a 400, so `not-a-bid` is rejected.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "total_results": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "bills": {
                          "type": "array",
                          "description": "Bills matching the filters.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "session_year": {},
                              "bid": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "bill_number": {},
                              "subject": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "latest_version_date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "issue": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "bill_full_text": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "chamber": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "status": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "last_modified": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "hearing_date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "party": {
                                "type": "array",
                                "description": "Distinct author parties. NO guaranteed order — legacy deduped through an unordered set, so compare as an unordered set."
                              },
                              "lead_authors": {
                                "type": "array",
                                "description": "Authors of the bill, with `party` removed from each entry.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "pid": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "party": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "first_name": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "last_name": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    }
                                  },
                                  "required": []
                                }
                              }
                            },
                            "required": [
                              "bill_number",
                              "lead_authors",
                              "party",
                              "session_year"
                            ]
                          }
                        }
                      },
                      "required": [
                        "bills"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/elasticsearch/hearings": {
      "get": {
        "operationId": "elasticsearchHearings",
        "summary": "/elasticsearch/hearings",
        "tags": [
          "Feeds"
        ],
        "description": "Requires a grant for resource `elasticsearch.hearings` at this version and jurisdiction.\n\nOne of the seven `elasticsearch/*` feeds. **These run no Elasticsearch** — the name describes the consumer. They are paginated SQL feeds meant to be walked incrementally by a search indexer, and they do not accept `session_year`.",
        "x-required-grant": "elasticsearch.hearings",
        "x-route-family": "feed",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "An unparseable value falls back to the default rather than erroring. `page=0` produces a negative offset and emits a bare LIMIT.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Capped at 1000 for most endpoints, 5000 for `elasticsearch/utterances` and `tipsheets/recent`, and 15000 for `elasticsearch/financials`. A decimal value falls back to the default rather than truncating.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "start_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "end_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally. **`end_date` on `elasticsearch/organizations` returns a 500** — a known defect, reproduced deliberately. Use `start_date`.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "hid[]",
            "in": "query",
            "required": false,
            "description": "Restrict to these hearing ids.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "total_results": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "hearings": {
                          "type": "array",
                          "description": "Hearings matching the filters.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "hid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "type": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "chamber": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "title": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "session_year": {},
                              "topic": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "last_modified": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "bills_discussed": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "Newline-joined bill descriptions for the hearing, or an empty string when none."
                              },
                              "cids": {
                                "type": "array",
                                "description": "Committee ids meeting at this hearing."
                              },
                              "pids": {
                                "type": "array",
                                "description": "Person ids appearing at this hearing."
                              },
                              "bids": {
                                "type": "array",
                                "description": "Bill ids discussed at this hearing."
                              }
                            },
                            "required": [
                              "bids",
                              "bills_discussed",
                              "cids",
                              "pids",
                              "session_year"
                            ]
                          }
                        }
                      },
                      "required": [
                        "hearings"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/elasticsearch/financials": {
      "get": {
        "operationId": "elasticsearchFinancials",
        "summary": "/elasticsearch/financials",
        "tags": [
          "Feeds"
        ],
        "description": "Requires a grant for resource `elasticsearch.financials` at this version and jurisdiction.\n\nOne of the seven `elasticsearch/*` feeds. **These run no Elasticsearch** — the name describes the consumer. They are paginated SQL feeds meant to be walked incrementally by a search indexer, and they do not accept `session_year`.",
        "x-required-grant": "elasticsearch.financials",
        "x-route-family": "feed",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "An unparseable value falls back to the default rather than erroring. `page=0` produces a negative offset and emits a bare LIMIT.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Capped at 1000 for most endpoints, 5000 for `elasticsearch/utterances` and `tipsheets/recent`, and 15000 for `elasticsearch/financials`. A decimal value falls back to the default rather than truncating.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "start_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "end_date",
            "in": "query",
            "required": false,
            "description": "Unix timestamp, filtered against a last-touched column so the feed can be walked incrementally. **`end_date` on `elasticsearch/organizations` returns a 500** — a known defect, reproduced deliberately. Use `start_date`.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "giver[]",
            "in": "query",
            "required": false,
            "description": "Filter by giving entity, addressed as `pid-123` or `oid-456`. Pattern-validated; a bare `pid-` passes the pattern, fails to parse, and is silently dropped rather than filtering on id 0.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "receiver[]",
            "in": "query",
            "required": false,
            "description": "Filter by receiving entity, same `pid-` / `oid-` form as `giver[]`.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "transaction_type[]",
            "in": "query",
            "required": false,
            "description": "Filter by transaction type.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "candidate_donations",
                  "independent_expenditures",
                  "party_committees",
                  "gifts",
                  "sponsored_travel"
                ]
              }
            }
          },
          {
            "name": "year[]",
            "in": "query",
            "required": false,
            "description": "Filter by year. A year with no matching rows returns `total_contributions` of 0, not null.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              }
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "description": "Sort column, mapped through a whitelist before interpolation. `date` maps to TWO expressions so that NULL dates sort last.",
            "schema": {
              "type": "string",
              "enum": [
                "giver",
                "receiver",
                "transaction_type",
                "date",
                "amount"
              ],
              "default": "date"
            }
          },
          {
            "name": "sort_order",
            "in": "query",
            "required": false,
            "description": "Sort direction.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "total_results": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "financials": {
                          "type": "array",
                          "description": "Campaign-finance transactions. `giver` carries four keys (`entity_id`, `entity_type`, `label`, `specific_label`); `receiver` carries only three — it omits `specific_label` even though the query selects it. Preserved.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "fid": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "type": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "giver": {},
                              "receiver": {},
                              "date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "amount": {
                                "type": [
                                  "number",
                                  "null"
                                ]
                              },
                              "description": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "reimbursed": {
                                "type": [
                                  "boolean",
                                  "null"
                                ]
                              },
                              "last_modified": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              }
                            },
                            "required": [
                              "giver",
                              "receiver"
                            ]
                          }
                        }
                      },
                      "required": [
                        "financials"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/tipsheets/recent": {
      "get": {
        "operationId": "tipsheetsRecent",
        "summary": "/tipsheets/recent",
        "tags": [
          "Directory"
        ],
        "description": "Requires a grant for resource `tipsheets.recent` at this version and jurisdiction.",
        "x-required-grant": "tipsheets.recent",
        "x-route-family": "directory",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "session_year",
            "in": "query",
            "required": false,
            "description": "A two-year session named by its first year: 2023 means 2023-2024. Resolved to the jurisdiction's most recent session when omitted.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "An unparseable value falls back to the default rather than erroring. `page=0` produces a negative offset and emits a bare LIMIT.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Capped at 1000 for most endpoints, 5000 for `elasticsearch/utterances` and `tipsheets/recent`, and 15000 for `elasticsearch/financials`. A decimal value falls back to the default rather than truncating.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "total_results": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "tipsheets": {
                          "type": "array",
                          "description": "Recently published tipsheets, newest first. `tipsheet_tile_json` is an UNGUARDED `JSON.parse` of a text column, so a malformed value 500s the request — preserved.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "tile_bill_num": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "tile_committee": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "tile_date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "tile_date_line": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "tile_image_link": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "tile_title": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "tipsheet_tile_json": {}
                            },
                            "required": [
                              "tipsheet_tile_json"
                            ]
                          }
                        }
                      },
                      "required": [
                        "tipsheets"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/people/legislators/metrics": {
      "get": {
        "operationId": "peopleLegislatorsMetrics",
        "summary": "/people/legislators/metrics",
        "tags": [
          "Directory"
        ],
        "description": "Requires a grant for resource `people.legislators.metrics` at this version and jurisdiction.",
        "x-required-grant": "people.legislators.metrics",
        "x-route-family": "directory",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "session_year",
            "in": "query",
            "required": false,
            "description": "A two-year session named by its first year: 2023 means 2023-2024. Resolved to the jurisdiction's most recent session when omitted.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "An unparseable value falls back to the default rather than erroring. `page=0` produces a negative offset and emits a bare LIMIT.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Capped at 1000 for most endpoints, 5000 for `elasticsearch/utterances` and `tipsheets/recent`, and 15000 for `elasticsearch/financials`. A decimal value falls back to the default rather than truncating.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "total_results": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "metrics": {
                          "type": "array",
                          "description": "One entry per populated metric. A metric with no rows, or whose every score is 0.0, is dropped entirely rather than returned empty.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "score_type": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "filter_name": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "The metric description, used as its display name."
                              },
                              "type": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "Score type, from `score_type`."
                              },
                              "data": {
                                "type": "array",
                                "description": "Legislators ranked by this metric.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "pid": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "image": {},
                                    "first": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "last": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "city": {},
                                    "chamber": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "district": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "party": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "score": {
                                      "type": [
                                        "number",
                                        "null"
                                      ]
                                    }
                                  },
                                  "required": [
                                    "image",
                                    "city"
                                  ]
                                }
                              }
                            },
                            "required": [
                              "data",
                              "filter_name",
                              "type"
                            ]
                          }
                        }
                      },
                      "required": [
                        "metrics"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/people/legislators": {
      "get": {
        "operationId": "peopleLegislators",
        "summary": "/people/legislators",
        "tags": [
          "Directory"
        ],
        "description": "Requires a grant for resource `people.legislators` at this version and jurisdiction.",
        "x-required-grant": "people.legislators",
        "x-route-family": "directory",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "session_year",
            "in": "query",
            "required": false,
            "description": "A two-year session named by its first year: 2023 means 2023-2024. Resolved to the jurisdiction's most recent session when omitted.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "An unparseable value falls back to the default rather than erroring. `page=0` produces a negative offset and emits a bare LIMIT.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Capped at 1000 for most endpoints, 5000 for `elasticsearch/utterances` and `tipsheets/recent`, and 15000 for `elasticsearch/financials`. A decimal value falls back to the default rather than truncating.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "pid[]",
            "in": "query",
            "required": false,
            "description": "Restrict to these legislators.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              }
            }
          },
          {
            "name": "chamber[]",
            "in": "query",
            "required": false,
            "description": "Restrict to these chambers.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "assembly",
                  "senate",
                  "joint"
                ]
              }
            }
          },
          {
            "name": "party[]",
            "in": "query",
            "required": false,
            "description": "Restrict to these parties. Note the value is echoed back under `filters.author_party` TITLE-CASED, and filtering to `democrat` alone decrements `total_results` while `total_pages` keeps the pre-decrement count — both deliberate, see docs/development/legacy-contract.md.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "democrat",
                  "republican",
                  "independent"
                ]
              }
            }
          },
          {
            "name": "district[]",
            "in": "query",
            "required": false,
            "description": "Restrict to these districts.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "integer"
              }
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "description": "Sort column, mapped through a strict allow-list before it reaches the SQL.",
            "schema": {
              "type": "string",
              "enum": [
                "first",
                "last",
                "district",
                "party"
              ],
              "default": "last"
            }
          },
          {
            "name": "sort_order",
            "in": "query",
            "required": false,
            "description": "Sort direction.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "total_results": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "legislators": {
                          "type": "array",
                          "description": "Legislators matching the filters. One party-switched legislator may be rewritten or dropped by a post-query fix-up — see docs/development/legacy-contract.md.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "pid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "image": {},
                              "first": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "last": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "city": {},
                              "district": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "party": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "chamber": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "age": {},
                              "birthplace": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "gender": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "race": {},
                              "sexual_orientation": {},
                              "high_school": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            },
                            "required": [
                              "age",
                              "city",
                              "image",
                              "race",
                              "sexual_orientation"
                            ]
                          }
                        }
                      },
                      "required": [
                        "legislators"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/bills/issues": {
      "get": {
        "operationId": "billsIssues",
        "summary": "/bills/issues",
        "tags": [
          "Directory"
        ],
        "description": "Requires a grant for resource `bills.issues` at this version and jurisdiction.",
        "x-required-grant": "bills.issues",
        "x-route-family": "directory",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "session_year",
            "in": "query",
            "required": false,
            "description": "A two-year session named by its first year: 2023 means 2023-2024. Resolved to the jurisdiction's most recent session when omitted.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "An unparseable value falls back to the default rather than erroring. `page=0` produces a negative offset and emits a bare LIMIT.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "per_page",
            "in": "query",
            "required": false,
            "description": "Capped at 1000 for most endpoints, 5000 for `elasticsearch/utterances` and `tipsheets/recent`, and 15000 for `elasticsearch/financials`. A decimal value falls back to the default rather than truncating.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          },
          {
            "name": "issue",
            "in": "query",
            "required": false,
            "description": "Issue name. Dashes become spaces and the value is title-cased before matching, so `public-safety` matches the stored \"Public Safety\". An unmatched issue returns empty buckets rather than an error.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "bill[]",
            "in": "query",
            "required": false,
            "description": "Bill ids to feature. One query per id, concatenated in request order. Regex-validated as `^\\w+$`, so a hyphen is a 400.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "integer"
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total_pages": {
                      "type": "integer"
                    },
                    "total_results": {
                      "type": "integer"
                    },
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "featured_bills": {
                          "type": "array",
                          "description": "Bills named by `bill[]`, one query each, in the order requested. Empty when `bill[]` is absent.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "bid": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "bill_number": {},
                              "title": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "simple_status": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "pid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "image": {},
                              "first": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "last": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "party": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            },
                            "required": [
                              "bill_number",
                              "image"
                            ]
                          }
                        },
                        "bills": {
                          "type": "array",
                          "description": "Bills for the issue, concatenated one status bucket at a time — so the array is grouped by status rather than sorted across the whole set.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "bid": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "bill_number": {},
                              "title": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "simple_status": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "committee": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "Name of the committee that introduced the bill, from a per-bill query. `null` when there is none."
                              }
                            },
                            "required": [
                              "bill_number",
                              "committee"
                            ]
                          }
                        }
                      },
                      "required": [
                        "bills",
                        "featured_bills"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/tipsheet/{tid}": {
      "get": {
        "operationId": "tipsheet",
        "summary": "/tipsheet/:tid",
        "tags": [
          "Entity"
        ],
        "description": "Requires a grant for resource `tipsheet` at this version and jurisdiction.",
        "x-required-grant": "tipsheet",
        "x-route-family": "entity",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "tid",
            "in": "path",
            "required": true,
            "description": "Identifier from the URL. A value of the wrong type is a 400, not a 404.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "session_year",
            "in": "query",
            "required": false,
            "description": "A two-year session named by its first year: 2023 means 2023-2024. Resolved to the jurisdiction's most recent session when omitted.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "tipsheet": {
                          "type": "object",
                          "properties": {
                            "id": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "tipsheet_type": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "json": {}
                          },
                          "required": [
                            "json"
                          ],
                          "description": "A single tipsheet."
                        }
                      },
                      "required": [
                        "tipsheet"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/committee/{cid}": {
      "get": {
        "operationId": "committee",
        "summary": "/committee/:cid",
        "tags": [
          "Entity"
        ],
        "description": "Requires a grant for resource `committee` at this version and jurisdiction.",
        "x-required-grant": "committee",
        "x-route-family": "entity",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "cid",
            "in": "path",
            "required": true,
            "description": "Identifier from the URL. A value of the wrong type is a 400, not a 404.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "session_year",
            "in": "query",
            "required": false,
            "description": "A two-year session named by its first year: 2023 means 2023-2024. Resolved to the jurisdiction's most recent session when omitted.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "committee": {
                          "type": "object",
                          "properties": {
                            "cid": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "name": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "room": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "phone": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "fax": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "state": {
                              "type": [
                                "string",
                                "null"
                              ]
                            }
                          },
                          "description": "The committee itself."
                        },
                        "members": {
                          "type": "array",
                          "description": "Current members. Party is resolved as of TODAY for this route, unlike the vote and testimony routes, which use the row's own date.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "pid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "image": {},
                              "first": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "last": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "chair": {},
                              "sub_chair": {},
                              "party": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "district": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            },
                            "required": [
                              "chair",
                              "image",
                              "sub_chair"
                            ]
                          }
                        },
                        "hearings": {
                          "type": "array",
                          "description": "One entry per session year the committee sat, each wrapping its own hearings. A session year with no hearings is dropped entirely.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "session_year": {},
                              "cid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "data": {
                                "type": "array",
                                "description": "Hearings in that session year, each with an added `length` and `uid` from per-hearing queries.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "image_url": {},
                                    "hid": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "date": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "hearing_type": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "subject": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "bill_number": {},
                                    "start_time": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "file_id": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    }
                                  },
                                  "required": [
                                    "image_url",
                                    "bill_number"
                                  ]
                                }
                              }
                            },
                            "required": [
                              "data",
                              "session_year"
                            ]
                          }
                        }
                      },
                      "required": [
                        "committee",
                        "hearings",
                        "members"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/organization/{oid}": {
      "get": {
        "operationId": "organization",
        "summary": "/organization/:oid",
        "tags": [
          "Entity"
        ],
        "description": "Requires a grant for resource `organization` at this version and jurisdiction.",
        "x-required-grant": "organization",
        "x-route-family": "entity",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "oid",
            "in": "path",
            "required": true,
            "description": "Identifier from the URL. A value of the wrong type is a 400, not a 404.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "session_year",
            "in": "query",
            "required": false,
            "description": "A two-year session named by its first year: 2023 means 2023-2024. Resolved to the jurisdiction's most recent session when omitted.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "organization": {
                          "type": "object",
                          "properties": {
                            "oid": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "name": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "aliases": {
                              "type": "array",
                              "description": "Other names for the organization, excluding its own. NO guaranteed order — compare as an unordered set."
                            }
                          },
                          "required": [
                            "aliases"
                          ],
                          "description": "The organization itself. Only NEGATIVE `oid`s address one — a positive id 404s without any query."
                        },
                        "latest_activity": {
                          "type": "array",
                          "description": "The organization's most recent activity.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "activity_date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "activity_text": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "entity_type": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "entity_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "uid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "file_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "start_time": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              }
                            }
                          }
                        },
                        "past_affiliations": {
                          "type": "array",
                          "description": "Past affiliations, GROUPED by session year after serializing.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "year": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "subtitle": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "affiliationText": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "entityType": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "entityId": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            }
                          }
                        },
                        "testimonies": {
                          "type": "array",
                          "description": "One entry per session year with testimony. A year is dropped when its list is empty OR when every value of every entry is null or falsy.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "session_year": {},
                              "data": {
                                "type": "array",
                                "description": "Testimony in that session year, each with an added `length`.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "image_url": {},
                                    "hid": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "date": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "bill_number": {},
                                    "subject": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "uid": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "file_id": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "start_time": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    }
                                  },
                                  "required": [
                                    "image_url",
                                    "bill_number"
                                  ]
                                }
                              }
                            },
                            "required": [
                              "data",
                              "session_year"
                            ]
                          }
                        },
                        "org_alignments": {
                          "type": "array",
                          "description": "Bill positions, GROUPED by session year after serializing.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "session_year": {},
                              "bid": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "bill_name": {},
                              "last_recorded_alignment": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "alignments": {
                                "type": "array",
                                "description": "Per-bill alignment detail, bound to the ROW's own oid rather than the request's.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "alignment": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "alignment_date": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "hid": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "location": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    }
                                  },
                                  "required": []
                                }
                              }
                            },
                            "required": [
                              "alignments",
                              "bill_name",
                              "session_year"
                            ]
                          }
                        },
                        "lobbying_representation": {
                          "type": "array",
                          "description": "People who lobbied for this organization.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "pid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "lobbyist": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "bid": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "bill_number": {},
                              "hid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "hearing": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "file_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "uid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "start_time": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              }
                            },
                            "required": [
                              "bill_number"
                            ]
                          }
                        }
                      },
                      "required": [
                        "latest_activity",
                        "lobbying_representation",
                        "org_alignments",
                        "organization",
                        "past_affiliations",
                        "testimonies"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/hearing/{hid}": {
      "get": {
        "operationId": "hearing",
        "summary": "/hearing/:hid",
        "tags": [
          "Entity"
        ],
        "description": "Requires a grant for resource `hearing` at this version and jurisdiction.",
        "x-required-grant": "hearing",
        "x-route-family": "entity",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "hid",
            "in": "path",
            "required": true,
            "description": "Identifier from the URL. A value of the wrong type is a 400, not a 404.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "session_year",
            "in": "query",
            "required": false,
            "description": "A two-year session named by its first year: 2023 means 2023-2024. Resolved to the jurisdiction's most recent session when omitted.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "hearing": {
                          "type": "object",
                          "properties": {
                            "hid": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "date": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "state": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "session_year": {},
                            "title": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "hearing_video_thumbnail": {},
                            "hearing_videos": {
                              "type": "array",
                              "description": "Videos of the hearing. Each gains a `video_url` and LOSES `start_time`, which is deleted after serializing.",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "file_id": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "uid": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "start_time": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  }
                                },
                                "required": []
                              }
                            }
                          },
                          "required": [
                            "hearing_video_thumbnail",
                            "hearing_videos",
                            "session_year"
                          ],
                          "description": "The hearing itself."
                        },
                        "agenda": {
                          "type": "array",
                          "description": "One entry per bill discussed. `date` is declared on the schema but POPPED before the response — it doubles as the party-change comparison date. This is the query-densest object in the API: five further queries per item.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "did": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "bid": {},
                              "bill_number": {},
                              "subject": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "length": {
                                "type": [
                                  "integer",
                                  "null"
                                ],
                                "description": "Discussion length in seconds."
                              },
                              "hearing_videos": {
                                "type": "array",
                                "description": "Videos covering this discussion.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "fileId": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "duration": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "uid": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    }
                                  },
                                  "required": []
                                }
                              },
                              "next_discussion": {
                                "type": "object",
                                "description": "The next hearing discussing this bill, plus a `uid`. `null` when there is none, and always `null` for the no-bill-discussed sentinel.",
                                "properties": {
                                  "date": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "hid": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "file_id": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "start_time": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  }
                                },
                                "required": []
                              },
                              "previous_discussion": {
                                "type": "object",
                                "description": "The previous such hearing, same shape as `next_discussion`.",
                                "properties": {
                                  "date": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "hid": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "file_id": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "start_time": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  }
                                },
                                "required": []
                              },
                              "vote": {
                                "type": "object",
                                "description": "Vote summary plus a `vote_detail` array. An empty OBJECT — not `null` — when there was no vote.",
                                "properties": {
                                  "ayes": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "naes": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "abstain": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "result": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  }
                                },
                                "required": []
                              },
                              "speakers": {
                                "type": "object",
                                "description": "Speakers GROUPED by `person_type`, with that key deleted from each entry, so this is an object of arrays rather than a flat list."
                              }
                            },
                            "required": [
                              "bid",
                              "bill_number",
                              "hearing_videos",
                              "length",
                              "next_discussion",
                              "previous_discussion",
                              "speakers",
                              "vote"
                            ]
                          }
                        }
                      },
                      "required": [
                        "agenda",
                        "hearing"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/bill/{bid}": {
      "get": {
        "operationId": "bill",
        "summary": "/bill/:bid",
        "tags": [
          "Entity"
        ],
        "description": "Requires a grant for resource `bill` at this version and jurisdiction.",
        "x-required-grant": "bill",
        "x-route-family": "entity",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "bid",
            "in": "path",
            "required": true,
            "description": "Identifier from the URL. A value of the wrong type is a 400, not a 404.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "session_year",
            "in": "query",
            "required": false,
            "description": "A two-year session named by its first year: 2023 means 2023-2024. Resolved to the jurisdiction's most recent session when omitted.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "bill": {
                          "type": "object",
                          "properties": {
                            "bid": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "bill_number": {},
                            "subject": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "house": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "session_year": {},
                            "authors": {
                              "type": "array",
                              "description": "Lead authors, each with an added `party` (as of that row's own authorship date, not today) and `versions`.",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "pid": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "image": {},
                                  "first": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "last": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  }
                                },
                                "required": [
                                  "image"
                                ]
                              }
                            },
                            "co-authors": {
                              "type": "array",
                              "description": "Coauthors, same shape as `authors`. Note the HYPHEN in the key.",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "pid": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "image": {},
                                  "first": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "last": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  }
                                },
                                "required": [
                                  "image"
                                ]
                              }
                            },
                            "status_history": {
                              "type": "object",
                              "description": "Always `{progress, status, status_description}`. Empty/null for a bill before the 2023 session, which short-circuits without querying."
                            }
                          },
                          "required": [
                            "authors",
                            "bill_number",
                            "co-authors",
                            "session_year",
                            "status_history"
                          ],
                          "description": "The bill itself. `raw_session_year` is selected, used to pick the status-history cutoff, and then DELETED before the response — see BillPayload in helper.ts."
                        },
                        "bill_digest": {
                          "type": "array",
                          "description": "One entry per version that carries a digest. A projection, not a full dump.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "version": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "bill_state": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "digest": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            }
                          }
                        },
                        "bill_text": {
                          "type": "array",
                          "description": "One entry per version that carries full text. Same projection as `bill_digest`, with `text` in place of `digest`.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "version": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "bill_state": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "text": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            }
                          }
                        },
                        "latest_version_date": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Date of the most recent version, or `null`. A failed query yields `null` rather than an error."
                        },
                        "status": {
                          "type": "array",
                          "description": "The bill's action history.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "text": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            }
                          }
                        },
                        "votes": {
                          "type": "array",
                          "description": "Floor and committee votes.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "ayes": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "naes": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "abstain": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "result": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "text": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "location": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "vote_detail": {
                                "type": "array",
                                "description": "Per-legislator votes, with party resolved as of the VOTE's date rather than today.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "pid": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "first": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "last": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "image": {},
                                    "result": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "party": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    }
                                  },
                                  "required": [
                                    "image"
                                  ]
                                }
                              }
                            },
                            "required": [
                              "vote_detail"
                            ]
                          }
                        },
                        "org_alignment": {
                          "type": "array",
                          "description": "Organization positions, each MERGED with its hearing-testimony row so the three testimony keys sit alongside the alignment keys. A positive `oid` is blanked to `null` — the id is only meaningful for an OrgConcept. A failed query yields `[]`.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "oid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "org": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "alignment": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "alignment_date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "hid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "written_testimony_id": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "analysis_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "alignment_from": {},
                              "source_page_start": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "source_page_end": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "uid": {
                                "type": [
                                  "integer",
                                  "null"
                                ],
                                "description": "Testimony utterance id, or `null` when there is none."
                              },
                              "file_id": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "description": "Testimony video file id, or `null`."
                              },
                              "start_time": {
                                "type": [
                                  "integer",
                                  "null"
                                ],
                                "description": "Testimony offset in seconds, or `null`."
                              }
                            },
                            "required": [
                              "alignment_from",
                              "file_id",
                              "start_time",
                              "uid"
                            ]
                          }
                        },
                        "hearings": {
                          "type": "array",
                          "description": "Hearings at which the bill was discussed.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "image_url": {},
                              "hid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "committee": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "file_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "start_time": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "length": {
                                "type": [
                                  "integer",
                                  "null"
                                ],
                                "description": "Discussion length in seconds, from a per-hearing walk."
                              },
                              "uid": {
                                "type": [
                                  "integer",
                                  "null"
                                ],
                                "description": "First utterance of the discussion."
                              }
                            },
                            "required": [
                              "image_url",
                              "length",
                              "uid"
                            ]
                          }
                        },
                        "analysis": {
                          "type": "array",
                          "description": "Committee analyses of the bill.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "analysis_type": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "text": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "analysis_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            }
                          }
                        },
                        "written_testimony": {
                          "type": "array",
                          "description": "Written testimony submitted on the bill.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "written_testimony_id": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "source_date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "source_name": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "source_url": {}
                            },
                            "required": [
                              "source_url"
                            ]
                          }
                        }
                      },
                      "required": [
                        "analysis",
                        "bill",
                        "bill_digest",
                        "bill_text",
                        "hearings",
                        "latest_version_date",
                        "org_alignment",
                        "status",
                        "votes",
                        "written_testimony"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/person/official/{pid}": {
      "get": {
        "operationId": "personOfficial",
        "summary": "/person/official/:pid",
        "tags": [
          "Entity"
        ],
        "description": "Requires a grant for resource `person.official` at this version and jurisdiction.",
        "x-required-grant": "person.official",
        "x-route-family": "entity",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "pid",
            "in": "path",
            "required": true,
            "description": "Identifier from the URL. A value of the wrong type is a 400, not a 404.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "session_year",
            "in": "query",
            "required": false,
            "description": "A two-year session named by its first year: 2023 means 2023-2024. Resolved to the jurisdiction's most recent session when omitted.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "profile": {
                          "type": "object",
                          "properties": {
                            "age": {}
                          },
                          "required": [
                            "age"
                          ],
                          "description": "The official themselves. KNOWN BUG (preserved): indexed without an empty check, so an official who passes the existence gate but has no profile row 500s."
                        },
                        "office": {
                          "type": "object",
                          "properties": {
                            "previous_election": {
                              "type": "object",
                              "description": "Always `{election_year, note, …}`. The explanatory `note` is only queried when there are no candidates.",
                              "properties": {
                                "candidate_name": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                },
                                "candidate_party": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                },
                                "candidate_is_incumbent": {
                                  "type": [
                                    "integer",
                                    "null"
                                  ]
                                },
                                "candidate_is_winner": {
                                  "type": [
                                    "integer",
                                    "null"
                                  ]
                                },
                                "candidate_pct_vote": {
                                  "type": [
                                    "number",
                                    "null"
                                  ]
                                }
                              },
                              "required": []
                            }
                          },
                          "required": [
                            "previous_election"
                          ],
                          "description": "A wrapper carrying only `previous_election`."
                        },
                        "financials": {
                          "type": "object",
                          "properties": {
                            "election": {
                              "type": "object",
                              "description": "Candidate donations, party committees, independent expenditures and grand total, per election year."
                            },
                            "influence": {
                              "type": "object",
                              "description": "Personal gifts, behests and sponsored travel, per influence year."
                            }
                          },
                          "required": [
                            "election",
                            "influence"
                          ],
                          "description": "Two halves, `election` and `influence`, each `{attributes, data}`. `attributes` holds display titles from the API_Titles table, not response data."
                        },
                        "bill_alignment": {
                          "type": "object",
                          "description": "Organization positions on bills this official authored. The ONE key on this route wrapped in a further `{data: […]}`.",
                          "properties": {
                            "data": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "oid": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "org": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "alignment": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "alignment_date": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "bid": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "bill_type": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "bill_number": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "analysis_flag": {
                                    "type": [
                                      "boolean",
                                      "null"
                                    ]
                                  }
                                }
                              }
                            }
                          },
                          "required": [
                            "data"
                          ]
                        }
                      },
                      "required": [
                        "bill_alignment",
                        "financials",
                        "office",
                        "profile"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/person/legislator/{pid}": {
      "get": {
        "operationId": "personLegislator",
        "summary": "/person/legislator/:pid",
        "tags": [
          "Entity"
        ],
        "description": "Requires a grant for resource `person.legislator` at this version and jurisdiction.",
        "x-required-grant": "person.legislator",
        "x-route-family": "entity",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "pid",
            "in": "path",
            "required": true,
            "description": "Identifier from the URL. A value of the wrong type is a 400, not a 404.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "session_year",
            "in": "query",
            "required": false,
            "description": "A two-year session named by its first year: 2023 means 2023-2024. Resolved to the jurisdiction's most recent session when omitted.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "alignment_meter": {
                          "type": "object",
                          "properties": {
                            "data": {
                              "type": "array",
                              "description": "Interest groups, each with an added `alignment_string` — a pre-rendered HTML blurb the website prints as-is.",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "oid": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "org_name": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "alignment_opportunities": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "percentage": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "votes": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "donation_amount": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "earliest_year_alignment": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "latest_year_alignment": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "earliest_year_donation": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "latest_year_donation": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  }
                                },
                                "required": []
                              }
                            },
                            "attributes": {
                              "type": "object",
                              "description": "Display titles from API_Titles."
                            }
                          },
                          "required": [
                            "attributes",
                            "data"
                          ],
                          "description": "`{data, attributes}`. `data` is the org list; `attributes` is display titles."
                        },
                        "bill_activity": {
                          "type": "object",
                          "properties": {
                            "data": {
                              "type": "object",
                              "description": "Stat description -> value."
                            },
                            "attributes": {
                              "type": "object",
                              "description": "Display titles from API_Titles."
                            }
                          },
                          "required": [
                            "attributes",
                            "data"
                          ],
                          "description": "Counts keyed by their own `description` text, so the KEYS are data-driven rather than fixed. A missing stat leaves the summary sentence null rather than failing."
                        },
                        "bills_authored": {
                          "type": "object",
                          "description": "Bills authored in the session year. One of the three keys on this route wrapped in a further `{data: […]}`.",
                          "properties": {
                            "data": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "bid": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "date": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "bill_number": {},
                                  "subject": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "status": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "committee": {
                                    "type": [
                                      "string",
                                      "null"
                                    ],
                                    "description": "Introducing committee name, from a per-bill query. `null` when none."
                                  }
                                },
                                "required": [
                                  "bill_number",
                                  "committee"
                                ]
                              }
                            }
                          },
                          "required": [
                            "data"
                          ]
                        },
                        "committees": {
                          "type": "object",
                          "description": "Committees served on in the session year. Also `{data: […]}`-wrapped.",
                          "properties": {
                            "data": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "cid": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "type": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "name": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "chair": {},
                                  "sub_chair": {},
                                  "issue": {}
                                },
                                "required": [
                                  "chair",
                                  "issue",
                                  "sub_chair"
                                ]
                              }
                            }
                          },
                          "required": [
                            "data"
                          ]
                        },
                        "district": {
                          "type": "object",
                          "properties": {
                            "attributes": {
                              "type": "object",
                              "description": "Hardcoded display titles."
                            },
                            "map": {
                              "type": "object",
                              "description": "District map. `map_url` carries a dumpDefault, so it is present even when the district has no map row.",
                              "properties": {
                                "district_text": {},
                                "district_subtext": {},
                                "map_url": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                }
                              },
                              "required": [
                                "district_text",
                                "district_subtext",
                                "map_url"
                              ]
                            },
                            "previous_election": {
                              "type": "object",
                              "description": "Previous election result, or the no-election note.",
                              "properties": {
                                "candidate_name": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                },
                                "candidate_party": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                },
                                "candidate_is_incumbent": {
                                  "type": [
                                    "integer",
                                    "null"
                                  ]
                                },
                                "candidate_is_winner": {
                                  "type": [
                                    "integer",
                                    "null"
                                  ]
                                },
                                "candidate_pct_vote": {
                                  "type": [
                                    "number",
                                    "null"
                                  ]
                                }
                              },
                              "required": []
                            },
                            "party_registration": {
                              "type": "array",
                              "description": "Party registration figures for the district.",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "democrats_district": {
                                    "type": [
                                      "number",
                                      "null"
                                    ]
                                  },
                                  "republicans_district": {
                                    "type": [
                                      "number",
                                      "null"
                                    ]
                                  },
                                  "no_party_district": {
                                    "type": [
                                      "number",
                                      "null"
                                    ]
                                  },
                                  "democrats_state": {
                                    "type": [
                                      "number",
                                      "null"
                                    ]
                                  },
                                  "republicans_state": {
                                    "type": [
                                      "number",
                                      "null"
                                    ]
                                  },
                                  "no_party_state": {
                                    "type": [
                                      "number",
                                      "null"
                                    ]
                                  }
                                },
                                "required": []
                              }
                            },
                            "census_data": {
                              "type": "object",
                              "description": "District census figures, alongside the statewide comparison.",
                              "properties": {
                                "census_attribute": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                },
                                "census_attribute_group": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                },
                                "group_value": {
                                  "type": [
                                    "number",
                                    "null"
                                  ]
                                }
                              },
                              "required": []
                            }
                          },
                          "required": [
                            "attributes",
                            "census_data",
                            "map",
                            "party_registration",
                            "previous_election"
                          ],
                          "description": "The district block. Its `attributes` are HARDCODED in the handler rather than read from API_Titles, unlike every other block here."
                        },
                        "financials": {
                          "type": "object",
                          "properties": {
                            "election": {
                              "type": "object",
                              "description": "Donations, party committees, independent expenditures, grand total."
                            },
                            "influence": {
                              "type": "object",
                              "description": "Personal gifts, behests, sponsored travel."
                            }
                          },
                          "required": [
                            "election",
                            "influence"
                          ],
                          "description": "Three election windows plus the influence block, each `{attributes, data}` as on the official route."
                        },
                        "ideology": {
                          "type": "object",
                          "properties": {
                            "ideology": {
                              "type": [
                                "number",
                                "null"
                              ]
                            }
                          },
                          "description": "Ideology score plus a hardcoded histogram band. `position` is `null` when there is no score."
                        },
                        "interest_group_rankings": {
                          "type": "object",
                          "properties": {
                            "data": {
                              "type": "object",
                              "description": "The three buckets, each an array of rated orgs.",
                              "properties": {
                                "org_rating": {},
                                "org_name": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                },
                                "link_org": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                }
                              },
                              "required": [
                                "org_rating"
                              ]
                            }
                          },
                          "required": [
                            "data"
                          ],
                          "description": "Orgs bucketed by rating into `aligned` (> 0.5), `not_aligned`, and `unknown` (a null rating), all under `data`."
                        },
                        "testimonies": {
                          "type": "object",
                          "description": "Testimony given, each with an added `length`. The third `{data: […]}`-wrapped key.",
                          "properties": {
                            "data": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "image_url": {},
                                  "hid": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "date": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "bill_number": {},
                                  "subject": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "uid": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "file_id": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  },
                                  "start_time": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ]
                                  },
                                  "length": {
                                    "type": [
                                      "integer",
                                      "null"
                                    ],
                                    "description": "Discussion length in seconds."
                                  }
                                },
                                "required": [
                                  "bill_number",
                                  "image_url",
                                  "length"
                                ]
                              }
                            }
                          },
                          "required": [
                            "data"
                          ]
                        }
                      },
                      "required": [
                        "alignment_meter",
                        "bill_activity",
                        "bills_authored",
                        "committees",
                        "district",
                        "financials",
                        "ideology",
                        "interest_group_rankings",
                        "testimonies"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/legacy/{state}/person/{pid}": {
      "get": {
        "operationId": "person",
        "summary": "/person/:pid",
        "tags": [
          "Entity"
        ],
        "description": "Requires a grant for resource `person` at this version and jurisdiction.",
        "x-required-grant": "person",
        "x-route-family": "entity",
        "parameters": [
          {
            "name": "state",
            "in": "path",
            "required": true,
            "description": "Jurisdiction. Lowercase is canonical but the segment is case-insensitive, so `ca` and `CA` are the same request. Which ones you may read depends on your key — an ungranted jurisdiction is a 400, with the same message as a malformed one.",
            "schema": {
              "type": "string",
              "enum": [
                "ca",
                "hi",
                "ma",
                "ct",
                "tx"
              ]
            }
          },
          {
            "name": "pid",
            "in": "path",
            "required": true,
            "description": "Identifier from the URL. A value of the wrong type is a 400, not a 404.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "session_year",
            "in": "query",
            "required": false,
            "description": "A two-year session named by its first year: 2023 means 2023-2024. Resolved to the jurisdiction's most recent session when omitted.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Object keys are sorted. Array order comes from the query ORDER BY and is part of the contract, with two documented exceptions — see the legacy contract. A field whose source column is absent is OMITTED rather than null.",
                      "properties": {
                        "person": {
                          "type": "object",
                          "properties": {
                            "first": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "last": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "image": {},
                            "ptype": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "affiliation_summary": {
                              "type": "array",
                              "description": "Current affiliations, each with the session years it covers.",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "ptype": {
                                    "type": [
                                      "string",
                                      "null"
                                    ]
                                  }
                                },
                                "required": []
                              }
                            }
                          },
                          "required": [
                            "affiliation_summary",
                            "image"
                          ],
                          "description": "The person itself."
                        },
                        "latest_activity": {
                          "type": "array",
                          "description": "The person's most recent activity.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "activity_date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "activity_text": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "entity_type": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "entity_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "uid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "file_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "start_time": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              }
                            }
                          }
                        },
                        "past_affiliations": {
                          "type": "array",
                          "description": "One entry per year with past affiliations, each wrapping its own detail rows.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "year": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "data": {
                                "type": "array",
                                "description": "Affiliations in that year — or an empty OBJECT rather than an empty list when there are none. Preserved.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "subtitle": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "affiliationText": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "entityType": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "entityId": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    }
                                  },
                                  "required": []
                                }
                              }
                            },
                            "required": [
                              "data"
                            ]
                          }
                        },
                        "testimonies": {
                          "type": "array",
                          "description": "One entry per session year with testimony. A session year whose testimony list comes back empty is dropped entirely.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "session_year": {},
                              "data": {
                                "type": "array",
                                "description": "Testimony in that session year, each with an added `length`.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "image_url": {},
                                    "hid": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "date": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "bill_number": {},
                                    "subject": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "uid": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "file_id": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "start_time": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    }
                                  },
                                  "required": [
                                    "image_url",
                                    "bill_number"
                                  ]
                                }
                              }
                            },
                            "required": [
                              "data",
                              "session_year"
                            ]
                          }
                        },
                        "bills_authored": {
                          "type": "array",
                          "description": "One entry per session year with authored bills.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "session_year": {},
                              "data": {
                                "type": "array",
                                "description": "Bills authored in that session year.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "bid": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "bill_name": {},
                                    "bill_subject": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "status": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    }
                                  },
                                  "required": [
                                    "bill_name"
                                  ]
                                }
                              }
                            },
                            "required": [
                              "data",
                              "session_year"
                            ]
                          }
                        },
                        "lobbying_representation": {
                          "type": "array",
                          "description": "Organizations this person lobbied for.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "date": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "oid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "organization_name": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "bid": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "bill_number": {},
                              "hid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "hearing": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "file_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "uid": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "start_time": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              }
                            },
                            "required": [
                              "bill_number"
                            ]
                          }
                        }
                      },
                      "required": [
                        "bills_authored",
                        "latest_activity",
                        "lobbying_representation",
                        "past_affiliations",
                        "person",
                        "testimonies"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation failed. Also returned for an unknown or ungranted jurisdiction — deliberately a 400 rather than a 403 or 404, because integrations already branch on it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, unknown or revoked API key. The body is identical for all four, so it cannot be used to probe which keys exist. A key is `ddapi_` followed by 43 characters, an underscore, and an 8-character checksum — send it exactly as issued, since the checksum is verified on every request. Keys are also environment-specific.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key is not granted this resource, method or version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "The entity does not exist, or the URL is missing its version or jurisdiction segment, or the route is not implemented at this version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Limits are per API key: 100 per 10 seconds and 300 per minute on the standard tier. A SECOND limiter counts failed authentications per client IP rather than per key, at 10 per 10 seconds and 20 per minute — a retry loop on an invalid key trips it within seconds. A 429 following successful requests is the per-key limit; a 429 following a run of 401s is that one. Both send `Retry-After: 10`, the shortest wait that could succeed. Note limits are counted per Cloudflare location, so there is no remaining-count header — the binding does not expose one.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          },
          "500": {
            "description": "Server error. The body reads \"An error has occured.\" — the typo is in the current production response and is part of the contract.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "msg": {
                      "description": "A string for 401/403/404/500. For a 400 it is an object keyed by location (`path`, `query`, `path_and_query`), each holding field names mapped to messages."
                    }
                  },
                  "required": [
                    "msg"
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}
