{"openapi":"3.1.0","info":{"title":"Ridgeway Legislature Public API","version":"1.0.0","summary":"Read-only API for the Ridgeway Code of Statutes, penal charges (R.C.C.), and vehicle code charges (R.V.C. / VCC).","description":"Public, read-only JSON API for external applications — CAD/MDT systems, bots, and department tools.\n\n**No authentication is required.** All endpoints are GET/HEAD only and CORS-enabled for any origin.\n\n### API keys (optional)\nAdministrators can issue API keys in the Senate Portal (Administrator → Developer API) to identify partner apps. A key raises the caller's rate limit from the anonymous 240/min to the key's own tier (default 1000/min) and may be **scoped**: full-access keys reach everything; scoped keys are limited to `penal`, `vcc`, and/or `statutes` endpoint groups (the combined `/charges` endpoints require both `penal` and `vcc`). Meta endpoints are always reachable with any valid key.\nSend the key as `Authorization: Bearer rk_…` or `X-API-Key: rk_…`. A malformed/unknown/revoked key is `401 invalid_api_key`; a valid key calling outside its scopes is `403 insufficient_scope`. Omit the header entirely for anonymous access.\n\n### Stable identifiers\nCharges are permanently identified by `type` + `code` (e.g. penal `1.01`) or by their `citation` (`R.C.C. § 1.01`).\nNumeric `id` values are internal database keys and **reset whenever the statute registry is re-imported** — never persist them.\n\n### Caching\nData responses carry a registry-wide `ETag` and `Cache-Control: public, max-age=60`. Send `If-None-Match` to receive `304 Not Modified` until any statute changes.\n\n### Rate limits\n240 requests per minute per IP for anonymous traffic; keyed traffic gets a per-key bucket at the key's own limit. `429` responses include `Retry-After`; every response except `OPTIONS` preflights (which are exempt from rate limiting) includes `X-RateLimit-*` headers reflecting the applied tier.\n\n### Conventions\nJail durations are **minutes** (in-game). `fine` is a number in dollars, or `null` when there is no fine or the penalty is non-monetary (see the human-readable `penalty` string, e.g. \"Forfeiture of All Assets\").\nAppend `?pretty` to any endpoint for indented JSON.","contact":{"name":"Ridgeway State Senate","url":"/citizens"}},"servers":[{"url":"/api/v1","description":"This deployment"}],"security":[{},{"bearerKey":[]},{"headerKey":[]}],"tags":[{"name":"Penal charges","description":"Ridgeway Criminal Code (R.C.C.) — chargeable criminal offenses with class and penalties."},{"name":"VCC charges","description":"Ridgeway Vehicle Code (R.V.C.) — chargeable traffic offenses (\"VCC\" items)."},{"name":"All charges","description":"Both code books in one list, discriminated by `type`."},{"name":"Statutes","description":"The full Ridgeway Code of Statutes registry: titles → chapters → sections."},{"name":"Meta","description":"Service discovery, dataset info, and health."}],"paths":{"/":{"get":{"tags":["Meta"],"operationId":"getIndex","summary":"API index — links to every endpoint","responses":{"200":{"description":"Endpoint directory.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Index"}}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Unusable identifier or invalid query parameter (e.g. no code-like content, NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/health":{"get":{"tags":["Meta"],"operationId":"getHealth","summary":"Liveness + database connectivity","responses":{"200":{"description":"Service healthy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"503":{"description":"Database unreachable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/meta":{"get":{"tags":["Meta"],"operationId":"getMeta","summary":"Dataset edition, counts, and last-updated timestamp","description":"Use `last_updated` (or the response ETag) to know when to refresh synced copies of the charge lists. `last_updated` covers edits to titles, chapters, and sections — the same inputs as the ETag.","responses":{"200":{"description":"Dataset metadata.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Meta"}}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Unusable identifier or invalid query parameter (e.g. no code-like content, NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/openapi.json":{"get":{"tags":["Meta"],"operationId":"getOpenApi","summary":"This document","description":"Served with a deployment-derived ETag — it changes when the API contract changes, not when statutes do.","responses":{"200":{"description":"OpenAPI 3.1 document.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{}}},"304":{"description":"Not modified — the client's If-None-Match ETag matches the deployed contract."},"400":{"description":"Malformed request (e.g. NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (240 requests/minute per IP).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/penal/charges":{"get":{"tags":["Penal charges"],"operationId":"listPenalCharges","summary":"List penal charges (R.C.C.)","description":"All chargeable criminal offenses. Without `limit`, the complete book is returned (currently 110 active charges) — ideal for syncing into a CAD.","parameters":[{"$ref":"#/components/parameters/q"},{"$ref":"#/components/parameters/class"},{"$ref":"#/components/parameters/category"},{"$ref":"#/components/parameters/codes"},{"$ref":"#/components/parameters/sort"},{"$ref":"#/components/parameters/order"},{"$ref":"#/components/parameters/includeInactive"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/offset"},{"$ref":"#/components/parameters/pretty"}],"responses":{"200":{"description":"Penal charges.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Charge"}},"meta":{"type":"object","description":"Pagination metadata for list responses.","properties":{"total":{"type":"integer","description":"Total rows matching the query, ignoring limit/offset."},"count":{"type":"integer","description":"Rows in this response."},"limit":{"type":["integer","null"],"description":"Applied limit; null when the full result set is returned."},"offset":{"type":"integer"},"query":{"type":"string","description":"Echo of the search terms (`/statutes/search` only)."}}}}},"example":{"data":[{"id":1412,"type":"penal","code":"1.01","citation":"R.C.C. § 1.01","name":"Treason","description":"Whoever levies war against the State of Ridgeway, adhering to its enemies, or giving them aid and comfort, owing allegiance to the state.","class":"felony","fine":null,"jail_minutes":120,"penalty":"Felony · Imprisonment: 120 minutes · Fine: Forfeiture of All Assets · Punishable by conviction","category":{"number":"X.1","name":"Crimes Against the State Government"},"book":{"abbr":"R.C.C.","name":"Ridgeway Criminal Code (R.C.C.)","title_number":"X"},"status":"active","updated_at":"2026-07-21T02:27:12.000Z"}],"meta":{"total":110,"count":1,"limit":1,"offset":0}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Invalid query parameter, or malformed request (e.g. NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/penal/charges/{code}":{"get":{"tags":["Penal charges"],"operationId":"getPenalCharge","summary":"One penal charge by code","parameters":[{"name":"code","in":"path","required":true,"description":"Charge code — `1.01`, `4.01(a)`, or a full citation like `R.C.C. § 1.01` (URL-encoded).","schema":{"type":"string"},"example":"1.01"},{"$ref":"#/components/parameters/includeInactive"},{"$ref":"#/components/parameters/pretty"}],"responses":{"200":{"description":"The charge.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Charge"}}},"example":{"data":{"id":1412,"type":"penal","code":"1.01","citation":"R.C.C. § 1.01","name":"Treason","description":"Whoever levies war against the State of Ridgeway, adhering to its enemies, or giving them aid and comfort, owing allegiance to the state.","class":"felony","fine":null,"jail_minutes":120,"penalty":"Felony · Imprisonment: 120 minutes · Fine: Forfeiture of All Assets · Punishable by conviction","category":{"number":"X.1","name":"Crimes Against the State Government"},"book":{"abbr":"R.C.C.","name":"Ridgeway Criminal Code (R.C.C.)","title_number":"X"},"status":"active","updated_at":"2026-07-21T02:27:12.000Z"}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Unusable identifier or invalid query parameter (e.g. no code-like content, NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No resource matches the given identifier.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/penal/categories":{"get":{"tags":["Penal charges"],"operationId":"listPenalCategories","summary":"Penal charge categories (chapters) with counts","responses":{"200":{"description":"Categories of the criminal code.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChargeCategory"}},"meta":{"type":"object","description":"Pagination metadata for list responses.","properties":{"total":{"type":"integer","description":"Total rows matching the query, ignoring limit/offset."},"count":{"type":"integer","description":"Rows in this response."},"limit":{"type":["integer","null"],"description":"Applied limit; null when the full result set is returned."},"offset":{"type":"integer"},"query":{"type":"string","description":"Echo of the search terms (`/statutes/search` only)."}}}}},"example":{"data":[{"number":"X.1","name":"Crimes Against the State Government","active_charges":15,"felonies":9,"misdemeanors":6,"infractions":0}],"meta":{"total":6,"count":6,"limit":null,"offset":0}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Invalid query parameter, or malformed request (e.g. NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/vcc/charges":{"get":{"tags":["VCC charges"],"operationId":"listVccCharges","summary":"List vehicle code charges (R.V.C. / VCC)","description":"All chargeable traffic offenses. Without `limit`, the complete book is returned (currently 56 active charges; 57 with `include_inactive=true`).","parameters":[{"$ref":"#/components/parameters/q"},{"$ref":"#/components/parameters/class"},{"$ref":"#/components/parameters/category"},{"$ref":"#/components/parameters/codes"},{"$ref":"#/components/parameters/sort"},{"$ref":"#/components/parameters/order"},{"$ref":"#/components/parameters/includeInactive"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/offset"},{"$ref":"#/components/parameters/pretty"}],"responses":{"200":{"description":"VCC charges.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Charge"}},"meta":{"type":"object","description":"Pagination metadata for list responses.","properties":{"total":{"type":"integer","description":"Total rows matching the query, ignoring limit/offset."},"count":{"type":"integer","description":"Rows in this response."},"limit":{"type":["integer","null"],"description":"Applied limit; null when the full result set is returned."},"offset":{"type":"integer"},"query":{"type":"string","description":"Echo of the search terms (`/statutes/search` only)."}}}}},"example":{"data":[{"id":1523,"type":"vcc","code":"1.01","citation":"R.V.C. § 1.01","name":"Speeding","description":"Whoever operates a motor vehicle in excess of 10 studs per second over the posted speed limit.","class":"infraction","fine":250,"jail_minutes":null,"penalty":"Civil Infraction · Fine: $250","category":{"number":"XI.1","name":"Speeding Offenses"},"book":{"abbr":"R.V.C.","name":"Ridgeway Vehicle Code (R.V.C.)","title_number":"XI"},"status":"active","updated_at":"2026-07-21T02:27:12.000Z"}],"meta":{"total":56,"count":1,"limit":1,"offset":0}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Invalid query parameter, or malformed request (e.g. NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/vcc/charges/{code}":{"get":{"tags":["VCC charges"],"operationId":"getVccCharge","summary":"One VCC charge by code","parameters":[{"name":"code","in":"path","required":true,"description":"Charge code — `1.01` or a full citation like `R.V.C. § 1.01` (URL-encoded).","schema":{"type":"string"},"example":"1.01"},{"$ref":"#/components/parameters/includeInactive"},{"$ref":"#/components/parameters/pretty"}],"responses":{"200":{"description":"The charge.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Charge"}}},"example":{"data":{"id":1523,"type":"vcc","code":"1.01","citation":"R.V.C. § 1.01","name":"Speeding","description":"Whoever operates a motor vehicle in excess of 10 studs per second over the posted speed limit.","class":"infraction","fine":250,"jail_minutes":null,"penalty":"Civil Infraction · Fine: $250","category":{"number":"XI.1","name":"Speeding Offenses"},"book":{"abbr":"R.V.C.","name":"Ridgeway Vehicle Code (R.V.C.)","title_number":"XI"},"status":"active","updated_at":"2026-07-21T02:27:12.000Z"}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Unusable identifier or invalid query parameter (e.g. no code-like content, NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No resource matches the given identifier.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/vcc/categories":{"get":{"tags":["VCC charges"],"operationId":"listVccCategories","summary":"VCC charge categories (chapters) with counts","responses":{"200":{"description":"Categories of the vehicle code.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChargeCategory"}},"meta":{"type":"object","description":"Pagination metadata for list responses.","properties":{"total":{"type":"integer","description":"Total rows matching the query, ignoring limit/offset."},"count":{"type":"integer","description":"Rows in this response."},"limit":{"type":["integer","null"],"description":"Applied limit; null when the full result set is returned."},"offset":{"type":"integer"},"query":{"type":"string","description":"Echo of the search terms (`/statutes/search` only)."}}}}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Invalid query parameter, or malformed request (e.g. NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/charges":{"get":{"tags":["All charges"],"operationId":"listAllCharges","summary":"List charges from both code books","description":"Union of penal and VCC charges, discriminated by `type`. Accepts every charge-list filter plus `type`.","parameters":[{"$ref":"#/components/parameters/type"},{"$ref":"#/components/parameters/q"},{"$ref":"#/components/parameters/class"},{"$ref":"#/components/parameters/category"},{"$ref":"#/components/parameters/codes"},{"$ref":"#/components/parameters/sort"},{"$ref":"#/components/parameters/order"},{"$ref":"#/components/parameters/includeInactive"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/offset"},{"$ref":"#/components/parameters/pretty"}],"responses":{"200":{"description":"Charges from both books.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Charge"}},"meta":{"type":"object","description":"Pagination metadata for list responses.","properties":{"total":{"type":"integer","description":"Total rows matching the query, ignoring limit/offset."},"count":{"type":"integer","description":"Rows in this response."},"limit":{"type":["integer","null"],"description":"Applied limit; null when the full result set is returned."},"offset":{"type":"integer"},"query":{"type":"string","description":"Echo of the search terms (`/statutes/search` only)."}}}}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Invalid query parameter, or malformed request (e.g. NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/charges/{citation}":{"get":{"tags":["All charges"],"operationId":"getChargeByCitation","summary":"Resolve one charge by citation","description":"Accepts `RCC 1.01`, `R.V.C. § 2.03`, `rvc-2.03`, or a bare code plus `?type=`. A bare code that exists in both books returns `409 ambiguous_code` with the candidates.","parameters":[{"name":"citation","in":"path","required":true,"schema":{"type":"string"},"example":"RCC 1.01"},{"$ref":"#/components/parameters/type"},{"$ref":"#/components/parameters/includeInactive"},{"$ref":"#/components/parameters/pretty"}],"responses":{"200":{"description":"The charge.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Charge"}}},"example":{"data":{"id":1412,"type":"penal","code":"1.01","citation":"R.C.C. § 1.01","name":"Treason","description":"Whoever levies war against the State of Ridgeway, adhering to its enemies, or giving them aid and comfort, owing allegiance to the state.","class":"felony","fine":null,"jail_minutes":120,"penalty":"Felony · Imprisonment: 120 minutes · Fine: Forfeiture of All Assets · Punishable by conviction","category":{"number":"X.1","name":"Crimes Against the State Government"},"book":{"abbr":"R.C.C.","name":"Ridgeway Criminal Code (R.C.C.)","title_number":"X"},"status":"active","updated_at":"2026-07-21T02:27:12.000Z"}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Unusable identifier or invalid query parameter (e.g. no code-like content, NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No resource matches the given identifier.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Bare code exists in both books — response includes `candidates`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/statutes/titles":{"get":{"tags":["Statutes"],"operationId":"listTitles","summary":"List all statute titles","responses":{"200":{"description":"All titles with chapter/section counts.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Title"}},"meta":{"type":"object","description":"Pagination metadata for list responses.","properties":{"total":{"type":"integer","description":"Total rows matching the query, ignoring limit/offset."},"count":{"type":"integer","description":"Rows in this response."},"limit":{"type":["integer","null"],"description":"Applied limit; null when the full result set is returned."},"offset":{"type":"integer"},"query":{"type":"string","description":"Echo of the search terms (`/statutes/search` only)."}}}}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Invalid query parameter, or malformed request (e.g. NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/statutes/titles/{number}":{"get":{"tags":["Statutes"],"operationId":"getTitle","summary":"One title with its chapters","parameters":[{"name":"number","in":"path","required":true,"description":"Roman-numeral title number (case-insensitive).","schema":{"type":"string"},"example":"X"},{"$ref":"#/components/parameters/pretty"}],"responses":{"200":{"description":"The title and its chapters.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TitleDetail"}}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Unusable identifier or invalid query parameter (e.g. no code-like content, NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No resource matches the given identifier.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/statutes/chapters/{number}/sections":{"get":{"tags":["Statutes"],"operationId":"listChapterSections","summary":"Sections of a chapter","parameters":[{"name":"number","in":"path","required":true,"description":"Chapter number, e.g. `6.1` or `X.2` (case-insensitive).","schema":{"type":"string"},"example":"X.2"},{"$ref":"#/components/parameters/includeInactive"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/offset"},{"$ref":"#/components/parameters/pretty"}],"responses":{"200":{"description":"Sections in chapter order.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Section"}},"meta":{"type":"object","description":"Pagination metadata for list responses.","properties":{"total":{"type":"integer","description":"Total rows matching the query, ignoring limit/offset."},"count":{"type":"integer","description":"Rows in this response."},"limit":{"type":["integer","null"],"description":"Applied limit; null when the full result set is returned."},"offset":{"type":"integer"},"query":{"type":"string","description":"Echo of the search terms (`/statutes/search` only)."}}}}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Invalid query parameter, or malformed request (e.g. NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/statutes/sections/{number}":{"get":{"tags":["Statutes"],"operationId":"getSection","summary":"One section by citation","description":"Whitespace- and §-insensitive. A suffix that matches exactly one section also resolves: `2.38` finds `R.C.C. § 2.38`. An ambiguous suffix (e.g. `111.001`, which ends nine different citations) returns `409` with `total_matches` and up to 10 `candidates`.","parameters":[{"name":"number","in":"path","required":true,"schema":{"type":"string"},"example":"6 R. Stat. § 111.001"},{"$ref":"#/components/parameters/pretty"}],"responses":{"200":{"description":"The section with title/chapter context.","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Section"}}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Unusable identifier or invalid query parameter (e.g. no code-like content, NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No resource matches the given identifier.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Citation suffix matches multiple sections — response includes `total_matches` and up to 10 `candidates`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/statutes/search":{"get":{"tags":["Statutes"],"operationId":"searchStatutes","summary":"Search the full registry","description":"Combines substring matching on number/name with Postgres full-text search on the body. Results are ranked: number/name matches first, then by text-search rank.","parameters":[{"name":"q","in":"query","required":true,"description":"Search terms.","schema":{"type":"string"},"example":"vehicle theft"},{"name":"classifier","in":"query","description":"Restrict to a classifier.","schema":{"type":"string","enum":["PEN","CIV","VEH"]}},{"name":"title","in":"query","description":"Restrict to a title number, e.g. `X`.","schema":{"type":"string"}},{"$ref":"#/components/parameters/includeInactive"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/offset"},{"$ref":"#/components/parameters/pretty"}],"responses":{"200":{"description":"Ranked matches (default limit 25, max 100).","headers":{"ETag":{"$ref":"#/components/headers/ETag"},"X-API-Version":{"$ref":"#/components/headers/XAPIVersion"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SearchResult"}},"meta":{"type":"object","description":"Pagination metadata for list responses.","properties":{"total":{"type":"integer","description":"Total rows matching the query, ignoring limit/offset."},"count":{"type":"integer","description":"Rows in this response."},"limit":{"type":["integer","null"],"description":"Applied limit; null when the full result set is returned."},"offset":{"type":"integer"},"query":{"type":"string","description":"Echo of the search terms (`/statutes/search` only)."}}}}}}}},"304":{"description":"Not modified — the client's If-None-Match ETag is still current."},"400":{"description":"Invalid query parameter, or malformed request (e.g. NUL bytes).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"An API key was presented but is malformed, unknown, or revoked (`invalid_api_key`). Anonymous requests never see this.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The presented API key lacks the scope for this endpoint (`insufficient_scope`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded — 240/min anonymous per IP, or the key's own limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server error — retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/statutes/pdf":{"get":{"tags":["Statutes"],"operationId":"getStatutePdf","summary":"Typeset PDF of the whole code","description":"Returns the entire Ridgeway Code of Statutes as a print-ready PDF, typeset in the style of the official United States Code annual edition. Cached and rebuilt only when the registry changes; shares the JSON ETag/304 contract (ETag carries a `-pdf` suffix). Served `inline` by default.","parameters":[{"name":"download","in":"query","description":"Present (any value except `false`) → `Content-Disposition: attachment`.","schema":{"type":"string"},"allowEmptyValue":true}],"responses":{"200":{"description":"The typeset PDF.","headers":{"ETag":{"description":"Weak dataset ETag with a `-pdf` suffix.","schema":{"type":"string"}},"X-PDF-Source":{"description":"`compiled` (built from the live registry) or `prebuilt` (committed fallback).","schema":{"type":"string","enum":["compiled","prebuilt"]}}},"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"304":{"description":"Not modified (registry unchanged since the client's ETag)."},"500":{"description":"PDF generation failed (`pdf_generation_failed`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"No LaTeX engine and no pre-built PDF available.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerKey":{"type":"http","scheme":"bearer","description":"API key issued in the Senate Portal (Administrator → Developer API): `Authorization: Bearer rk_<40 hex>`."},"headerKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Same key via a plain header, for clients that can't set Authorization."}},"headers":{"ETag":{"description":"Weak ETag. On data endpoints it is registry-wide and changes whenever any statute title/chapter/section is created, edited, or removed. The index (`/`) and `/openapi.json` instead carry a deployment-derived ETag that changes when the API contract changes.","schema":{"type":"string"}},"XAPIVersion":{"description":"API semantic version.","schema":{"type":"string"}}},"parameters":{"q":{"name":"q","in":"query","description":"Substring search over code, name, and description.","schema":{"type":"string"},"example":"murder"},"class":{"name":"class","in":"query","description":"Filter by offense class. Comma-separate for multiple: `felony,misdemeanor`.","schema":{"type":"string"},"example":"felony"},"category":{"name":"category","in":"query","description":"Filter by category (chapter) number — `X.2`, or bare `2` on single-book endpoints.","schema":{"type":"string"},"example":"X.2"},"codes":{"name":"codes","in":"query","description":"Batch-resolve up to 50 comma-separated codes: `codes=1.01,2.05`. Order of results follows code order, not input order. Entries that don't look like codes at all are a `400` naming them; well-formed codes that don't exist are simply absent from `data`. To detect absences, compare `meta.count` to your input size on the per-book endpoints — on the combined `/charges` a bare code can match both books, so verify against the returned `code` values instead.","schema":{"type":"string"},"example":"1.01,2.05"},"sort":{"name":"sort","in":"query","description":"Sort key. `code` (default) is book order; `fine` and `jail` sort nulls last.","schema":{"type":"string","enum":["code","name","class","fine","jail"],"default":"code"}},"order":{"name":"order","in":"query","schema":{"type":"string","enum":["asc","desc"],"default":"asc"}},"type":{"name":"type","in":"query","description":"Restrict to one code book. Canonical values are `penal` and `vcc`; `vehicle` and `traffic` are accepted aliases of `vcc`.","schema":{"type":"string","enum":["penal","vcc","vehicle","traffic"]}},"includeInactive":{"name":"include_inactive","in":"query","description":"Set `true` to include repealed/inactive entries (default returns only `active`).","schema":{"type":"string","enum":["true","false"],"default":"false"}},"limit":{"name":"limit","in":"query","description":"Max rows to return (charge/section lists cap at 500; search caps at 100). Omit on charge lists to get the full book.","schema":{"type":"integer","minimum":1}},"offset":{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},"pretty":{"name":"pretty","in":"query","description":"Present (any value except `false`) → indent the JSON response, on errors too.","schema":{"type":"boolean"},"allowEmptyValue":true}},"schemas":{"Charge":{"type":"object","description":"A chargeable offense from the R.C.C. (penal) or R.V.C. (vcc).","properties":{"id":{"type":"integer","description":"Internal database id. NOT stable across registry re-imports — key on `type` + `code` instead."},"type":{"type":"string","enum":["penal","vcc"],"description":"Which code book the charge belongs to."},"code":{"type":"string","description":"Bare charge code, unique within its book. Stable identifier.","examples":["1.01","4.01(a)"]},"citation":{"type":"string","description":"Full legal citation.","examples":["R.C.C. § 1.01"]},"name":{"type":"string","description":"Charge name.","examples":["Treason"]},"description":{"type":["string","null"],"description":"Statutory text of the offense."},"class":{"type":"string","enum":["felony","misdemeanor","infraction"]},"fine":{"type":["number","null"],"description":"Maximum fine in dollars. Null when no monetary fine applies — check `penalty` for non-monetary penalties (e.g. asset forfeiture)."},"jail_minutes":{"type":["integer","null"],"description":"Maximum imprisonment in minutes (in-game time). Null when the charge carries no jail time."},"penalty":{"type":["string","null"],"description":"Human-readable penalty summary exactly as published in the registry."},"category":{"type":"object","description":"The charge's category (registry chapter).","properties":{"number":{"type":"string"},"name":{"type":"string"}}},"book":{"type":"object","properties":{"abbr":{"type":"string","enum":["R.C.C.","R.V.C."]},"name":{"type":"string"},"title_number":{"type":"string","description":"Registry title housing this book (X or XI)."}}},"status":{"type":"string","description":"`active` unless `include_inactive=true` was used.","examples":["active"]},"updated_at":{"type":"string","format":"date-time"}}},"ChargeCategory":{"type":"object","properties":{"number":{"type":"string","examples":["X.1"]},"name":{"type":"string","examples":["Crimes Against the State Government"]},"active_charges":{"type":"integer"},"felonies":{"type":"integer"},"misdemeanors":{"type":"integer"},"infractions":{"type":"integer"}}},"Title":{"type":"object","properties":{"id":{"type":"integer","description":"Internal id — not stable across re-imports."},"number":{"type":"string","description":"Roman numeral.","examples":["X"]},"name":{"type":"string"},"classifier":{"type":["string","null"],"enum":["PEN","CIV","VEH",null],"description":"PEN = penal, CIV = civil, VEH = vehicle; null for structural titles."},"chapters":{"type":"integer"},"active_sections":{"type":"integer"},"chargeable_sections":{"type":"integer","description":"Sections that carry offense data (i.e. appear in the charge endpoints)."}}},"TitleDetail":{"type":"object","properties":{"id":{"type":"integer"},"number":{"type":"string"},"name":{"type":"string"},"classifier":{"type":["string","null"]},"chapters":{"type":"array","items":{"type":"object","properties":{"number":{"type":"string"},"name":{"type":"string"},"active_sections":{"type":"integer"}}}}}},"Section":{"type":"object","description":"A statute section with its title/chapter context.","properties":{"id":{"type":"integer","description":"Internal id — not stable across re-imports; key on `number`."},"number":{"type":"string","description":"Full citation — the stable identifier.","examples":["6 R. Stat. § 111.001"]},"name":{"type":"string"},"body":{"type":["string","null"]},"history":{"type":["string","null"],"description":"Legislative history (source bills)."},"notes":{"type":["string","null"]},"status":{"type":"string","examples":["active"]},"offense_class":{"type":["string","null"],"enum":["felony","misdemeanor","infraction",null]},"fine":{"type":["number","null"]},"jail_minutes":{"type":["integer","null"]},"chapter":{"type":"object","properties":{"number":{"type":"string"},"name":{"type":"string"}}},"title":{"type":"object","properties":{"number":{"type":"string"},"name":{"type":"string"},"classifier":{"type":["string","null"]}}},"updated_at":{"type":"string","format":"date-time"}}},"SearchResult":{"type":"object","description":"Search hit — a section summary with a highlighted snippet.","properties":{"id":{"type":"integer"},"number":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"offense_class":{"type":["string","null"]},"fine":{"type":["number","null"]},"jail_minutes":{"type":["integer","null"]},"chapter":{"type":"object","properties":{"number":{"type":"string"},"name":{"type":"string"}}},"title":{"type":"object","properties":{"number":{"type":"string"},"name":{"type":"string"},"classifier":{"type":["string","null"]}}},"snippet":{"type":"string","description":"Body excerpt with `<b>…</b>` around matched terms."}}},"Meta":{"type":"object","properties":{"edition":{"type":"string","examples":["2026 Ridgeway Code of Statutes"]},"counts":{"type":"object","properties":{"titles":{"type":"integer"},"chapters":{"type":"integer"},"sections_active":{"type":"integer"},"sections_total":{"type":"integer"},"penal_charges":{"type":"integer"},"vcc_charges":{"type":"integer"}}},"last_updated":{"type":["string","null"],"format":"date-time"},"api_version":{"type":"string"}}},"Health":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded"]},"database":{"type":"object","properties":{"connected":{"type":"boolean"},"latency_ms":{"type":"integer"}}},"uptime_seconds":{"type":"integer"},"version":{"type":"string"}}},"Index":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"},"documentation":{"type":"string"},"openapi":{"type":"string"},"endpoints":{"type":"object","additionalProperties":{"type":"string"}}}},"Error":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Machine-readable error code.","enum":["invalid_parameter","not_found","ambiguous_code","ambiguous_citation","rate_limited","method_not_allowed","internal_error","pdf_unavailable","pdf_generation_failed","invalid_api_key","insufficient_scope"]},"message":{"type":"string","description":"Human-readable explanation."},"param":{"type":"string","description":"Usually present on invalid_parameter — the failing parameter. Omitted when the whole request is malformed (e.g. NUL bytes)."},"candidates":{"type":"array","description":"Present on ambiguous_* — the matching resources (capped at 10 for ambiguous_citation)."},"total_matches":{"type":"integer","description":"Present on ambiguous_citation — total number of matching sections."}}}}}}}}