{
  "openapi": "3.1.0",
  "info": {
    "title": "Mining Incidents API",
    "summary": "Public read surface for every reportable MSHA mining-incident record (1983–present), plus email-alert subscriptions.",
    "description": "Stable API for miningincidents.org. Most endpoints (search, RSS feeds, map points, alerts subscribe) require no authentication. The /api/x402/* data endpoints are agent-payable over the x402 protocol (HTTP 402, USDC on Base, exact scheme). Full documentation: /docs.md.",
    "version": "1.2.0",
    "x-guidance": "Agent usage guide. The free surface: /api/map-points (all plottable mines), the RSS feeds under /recent, /state/{code}, /classification/{slug}, and /api/x402/fatalities/count (row count + column schema for any filter, no data values). Use the free count first to size a query and confirm total>0 before paying. The paid surface (x402): /api/x402/fatalities is the flagship — MSHA fatalities newest-first, including preliminary notices flagged source=preliminary that run 32-180 days ahead of the official dataset; 25 rows/page, follow next_page. /api/x402/fatality/{id}, /api/x402/mine/{id}, /api/x402/operator/{id} return single-entity facts plus aggregate totals. /api/x402/dossier/{id} ($2.00) bundles one operator into a single artifact: identity, mine list with status, full fatality event list, aggregate violation/penalty totals with 24-month trend direction, and plain-English flags. To pay: send an unpaid GET, read the x402 challenge from the 402 response, settle in USDC on Base, retry with the payment header. Scope: event facts and single-entity aggregate totals only. Per-citation line items, S&S/negligence/gravity fields, contest posture, rate-normalized trends and cross-entity benchmarks are not served here.",
    "contact": {
      "name": "Mining Incidents",
      "email": "hello@miningincidents.org",
      "url": "https://miningincidents.org"
    },
    "license": {
      "name": "Source: US MSHA public records (public domain)",
      "identifier": "CC0-1.0"
    }
  },
  "servers": [
    {
      "url": "https://miningincidents.org",
      "description": "production"
    }
  ],
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Stable short-code suitable for switching on."
          },
          "message": {
            "type": "string",
            "description": "Optional human-readable prose."
          }
        }
      },
      "MapPoint": {
        "type": "object",
        "required": [
          "mine_id",
          "name",
          "lat",
          "lon",
          "fatal_count"
        ],
        "properties": {
          "mine_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "lat": {
            "type": "number",
            "format": "double"
          },
          "lon": {
            "type": "number",
            "format": "double"
          },
          "fatal_count": {
            "type": "integer"
          }
        }
      },
      "SubscribeRequest": {
        "type": "object",
        "required": [
          "email",
          "kind"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "kind": {
            "type": "string",
            "enum": [
              "mine",
              "operator",
              "search"
            ]
          },
          "mine_id": {
            "type": "string"
          },
          "mine_name": {
            "type": "string"
          },
          "operator_id": {
            "type": "string"
          },
          "operator_name": {
            "type": "string"
          },
          "q": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "description": "Two-digit FIPS code"
          },
          "cal_yr": {
            "type": "integer"
          },
          "coal_metal_ind": {
            "type": "string",
            "enum": [
              "C",
              "M"
            ]
          },
          "fatal_only": {
            "type": "boolean"
          },
          "label": {
            "type": "string"
          }
        }
      },
      "SubscribeResponse": {
        "type": "object",
        "required": [
          "ok",
          "kind"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "kind": {
            "type": "string"
          },
          "already_subscribed": {
            "type": "boolean"
          }
        }
      }
    }
  },
  "paths": {
    "/openapi.json": {
      "get": {
        "summary": "This document.",
        "operationId": "getOpenAPI",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1 spec.",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/docs.md": {
      "get": {
        "summary": "Developer documentation (markdown).",
        "operationId": "getDocs",
        "security": [],
        "responses": {
          "200": {
            "description": "Markdown documentation.",
            "content": {
              "text/markdown": {}
            }
          }
        }
      }
    },
    "/health": {
      "get": {
        "summary": "Liveness probe used by Fly.",
        "operationId": "getHealth",
        "security": [],
        "responses": {
          "200": {
            "description": "Always 'ok'.",
            "content": {
              "text/plain": {}
            }
          }
        }
      }
    },
    "/api/map-points": {
      "get": {
        "summary": "Every plottable mine (mines with valid lat/lon).",
        "operationId": "getMapPoints",
        "security": [],
        "responses": {
          "200": {
            "description": "Array of MapPoint records (may be empty).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MapPoint"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/alerts/subscribe": {
      "post": {
        "summary": "Subscribe an email address to per-mine, per-operator, or saved-search alerts.",
        "description": "Sends a verification email; subscription only activates once the recipient clicks the link.",
        "operationId": "postAlertsSubscribe",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscribeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created or already-subscribed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscribeResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (invalid email, malformed JSON, missing kind-required field).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "DB write rejected.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Alerts disabled (service-role or mailer not configured on this instance).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/pageview": {
      "post": {
        "summary": "First-party pageview beacon.",
        "description": "Always 204; failures swallowed. Not intended for third-party use.",
        "operationId": "postPageview",
        "security": [],
        "responses": {
          "204": {
            "description": "Accepted."
          }
        }
      }
    },
    "/recent/feed.xml": {
      "get": {
        "summary": "RSS 2.0 feed of recent reportable accidents (all sectors, all states).",
        "operationId": "getRecentFeed",
        "security": [],
        "responses": {
          "200": {
            "description": "RSS XML.",
            "content": {
              "application/rss+xml": {}
            }
          }
        }
      }
    },
    "/state/{code}/feed.xml": {
      "get": {
        "summary": "RSS feed scoped to one state.",
        "operationId": "getStateFeed",
        "security": [],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "description": "Lowercase two-letter postal abbreviation (e.g. wv, ky, pa).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "RSS XML.",
            "content": {
              "application/rss+xml": {}
            }
          }
        }
      }
    },
    "/classification/{slug}/feed.xml": {
      "get": {
        "summary": "RSS feed scoped to one MSHA accident classification.",
        "operationId": "getClassificationFeed",
        "security": [],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Classification slug (e.g. powered-haulage, fall-of-roof-or-back).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "RSS XML.",
            "content": {
              "application/rss+xml": {}
            }
          }
        }
      }
    },
    "/api/x402": {
      "get": {
        "summary": "x402 discovery catalog: agent-payable data endpoints, prices and filters (free).",
        "description": "Lists the x402 paid endpoints (HTTP 402, USDC on Base, exact scheme). Free-tier facts only; no per-citation line items, S&S/negligence/gravity, contest posture, rate trends or benchmarks.",
        "operationId": "getX402Catalog",
        "security": [],
        "responses": {
          "200": {
            "description": "Discovery manifest.",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/api/x402/fatalities/count": {
      "get": {
        "summary": "Free count of MSHA fatalities (official + open preliminary) matching filters, plus the column schema of the paid feed.",
        "operationId": "getX402FatalitiesCount",
        "security": [],
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Lower bound on event date (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "description": "US state abbreviation (e.g. WV).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "commodity",
            "in": "query",
            "required": false,
            "description": "coal or metal.",
            "schema": {
              "type": "string",
              "enum": [
                "coal",
                "metal"
              ]
            }
          },
          {
            "name": "classification",
            "in": "query",
            "required": false,
            "description": "Case-insensitive substring match on MSHA accident classification (e.g. haulage, machinery, fall of).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{total, columns} — row count plus the field/column names of the paid feed. No data values.",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/api/x402/fatalities/sample": {
      "get": {
        "summary": "Free cached sample of the paid fatalities feed: the 10 most recent official MSHA fatality rows in the feed's exact shape, refreshed hourly. No filters, no paging.",
        "operationId": "getX402FatalitiesSample",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "{sample: true, sample_size, refreshed_at, rows} — the same row shape the paid feed returns, for the newest 10 official records only. Filters and paging are ignored; they belong to the paid feed.",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/api/x402/fatalities": {
      "get": {
        "summary": "Paid ($0.01) MSHA fatalities feed, newest first, including open preliminary notices flagged source=preliminary.",
        "description": "x402-gated. 25 rows/page; every response carries total_matches and next_page. Each official row includes latitude/longitude for mapping (null on preliminary rows). Preliminary rows are scraped 32-180 days ahead of the official MSHA dataset.",
        "operationId": "getX402Fatalities",
        "x-payment-info": {
          "price": {
            "currency": "USD",
            "mode": "fixed",
            "amount": "0.01"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "commodity",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "coal",
                "metal"
              ]
            }
          },
          {
            "name": "classification",
            "in": "query",
            "required": false,
            "description": "Case-insensitive substring match on MSHA accident classification.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Fatalities page.",
            "content": {
              "application/json": {}
            }
          },
          "402": {
            "description": "Payment required (x402 challenge in the PAYMENT-REQUIRED header)."
          },
          "404": {
            "description": "No rows match (never charged)."
          }
        }
      }
    },
    "/api/x402/fatality/{id}": {
      "get": {
        "summary": "Paid ($0.005) single MSHA fatality: facts, classification, narrative, entity links.",
        "operationId": "getX402Fatality",
        "x-payment-info": {
          "price": {
            "currency": "USD",
            "mode": "fixed",
            "amount": "0.005"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Numeric accident id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One fatality.",
            "content": {
              "application/json": {}
            }
          },
          "402": {
            "description": "Payment required."
          },
          "404": {
            "description": "Unknown id (never charged)."
          }
        }
      }
    },
    "/api/x402/mine/{id}": {
      "get": {
        "summary": "Paid ($0.01) mine profile: identity facts plus aggregate compliance and incident totals.",
        "operationId": "getX402Mine",
        "x-payment-info": {
          "price": {
            "currency": "USD",
            "mode": "fixed",
            "amount": "0.01"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Numeric MSHA mine id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Mine profile.",
            "content": {
              "application/json": {}
            }
          },
          "402": {
            "description": "Payment required."
          },
          "404": {
            "description": "Unknown id (never charged)."
          }
        }
      }
    },
    "/api/x402/mine/{id}/rates": {
      "get": {
        "summary": "Paid ($0.02) rate-normalized mine safety: fatality and violation rates per 200,000 employee-hours (MSHA/BLS basis), hours worked, low-exposure flags.",
        "operationId": "getX402MineRates",
        "x-payment-info": {
          "price": {
            "currency": "USD",
            "mode": "fixed",
            "amount": "0.02"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "MSHA 7-digit mine id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Mine safety rates.",
            "content": {
              "application/json": {}
            }
          },
          "402": {
            "description": "Payment required."
          },
          "404": {
            "description": "Unknown id (never charged)."
          }
        }
      }
    },
    "/api/x402/operator/{id}": {
      "get": {
        "summary": "Paid ($0.02) operator profile: identity plus aggregate totals plus fatality event list.",
        "operationId": "getX402Operator",
        "x-payment-info": {
          "price": {
            "currency": "USD",
            "mode": "fixed",
            "amount": "0.02"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "MSHA operator id: digits with an optional single-letter prefix, e.g. 1200001 or L10654.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Operator profile.",
            "content": {
              "application/json": {}
            }
          },
          "402": {
            "description": "Payment required."
          },
          "404": {
            "description": "Unknown id (never charged)."
          }
        }
      }
    },
    "/api/x402/operator/{id}/contact": {
      "get": {
        "summary": "Paid ($0.02) operator mailing address(es) of record: business name, contact title, location per distinct address; full street for corporate entities, city/state only for residential/individual rows.",
        "operationId": "getX402OperatorContact",
        "x-payment-info": {
          "price": {
            "currency": "USD",
            "mode": "fixed",
            "amount": "0.02"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "MSHA operator id: digits with an optional single-letter prefix, e.g. 1200001 or L10654.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Operator addresses of record.",
            "content": {
              "application/json": {}
            }
          },
          "402": {
            "description": "Payment required."
          },
          "404": {
            "description": "Unknown id (never charged)."
          }
        }
      }
    },
    "/api/x402/dossier/{id}": {
      "get": {
        "summary": "Paid ($2.00) operator safety dossier: identity, mine list with status, full fatality event list, aggregate violation/penalty totals with 24-month trend direction, plain-English flags.",
        "description": "x402-gated bundled artifact. Aggregates, trend directions and event lists only; no per-citation line items, S&S/negligence/gravity fields, contest posture or peer benchmarks.",
        "operationId": "getX402OperatorDossier",
        "x-payment-info": {
          "price": {
            "currency": "USD",
            "mode": "fixed",
            "amount": "2.00"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "MSHA operator id: digits with an optional single-letter prefix, e.g. 1200001 or L10654.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Operator safety dossier.",
            "content": {
              "application/json": {}
            }
          },
          "402": {
            "description": "Payment required."
          },
          "404": {
            "description": "Unknown id (never charged)."
          }
        }
      }
    },
    "/api/x402/resolve": {
      "get": {
        "summary": "Paid ($0.02) fuzzy entity resolver: approximate or misspelled operator/mine name to ranked canonical MSHA entity ids with aggregate summaries.",
        "description": "x402-gated. pg_trgm similarity match (floor 0.3) over operator and mine names; each match carries aggregate fatality/violation totals plus links to the human page and the matching paid endpoints. Built from paid feature request #1 on /api/x402/feature-requests.",
        "operationId": "getX402Resolve",
        "x-payment-info": {
          "price": {
            "currency": "USD",
            "mode": "fixed",
            "amount": "0.02"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": true,
            "description": "Approximate or misspelled operator or mine name, 2 to 120 characters.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 120
            }
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Filter to one entity kind; omit to search both.",
            "schema": {
              "type": "string",
              "enum": [
                "operator",
                "mine"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked matches with aggregate summaries.",
            "content": {
              "application/json": {}
            }
          },
          "402": {
            "description": "Payment required."
          },
          "404": {
            "description": "No match at or above the similarity floor (never charged)."
          }
        }
      }
    },
    "/api/x402/inspections": {
      "get": {
        "summary": "Paid ($0.01) MSHA inspection events, up to 25 newest-first: dates, activity type, inspector count, on-site and inspection hours, sample counts, active/idle sections.",
        "operationId": "getX402Inspections",
        "x-payment-info": {
          "price": {
            "currency": "USD",
            "mode": "fixed",
            "amount": "0.01"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "mine_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "operator_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "commodity",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "coal",
                "metal"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Inspection events.",
            "content": {
              "application/json": {}
            }
          },
          "402": {
            "description": "Payment required."
          }
        }
      }
    },
    "/api/x402/feature-requests": {
      "get": {
        "summary": "Free list of agent feature requests: what MSHA data/API features have been asked for and what shipped.",
        "description": "Free read side of the paid feature-request channel. Returns {id, text, upvotes, status, created_at} rows, most-upvoted then newest first, 25 per page. status tracks triage: new/planned/shipped/declined. Check here before paying to submit a duplicate.",
        "operationId": "getX402FeatureRequests",
        "security": [],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-based page number.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Feature-request list, 25 per page with next_page.",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/api/x402/feature-request": {
      "get": {
        "summary": "Paid ($0.01) feature request: tell Mining Incidents what MSHA data or API features you wish existed.",
        "description": "x402-gated write. Stores the request for human review; the cent is a spam gate and a demand signal, not revenue. Track its status on the free /api/x402/feature-requests list. Empty or over-long text returns 400 and is never charged.",
        "operationId": "submitX402FeatureRequest",
        "x-payment-info": {
          "price": {
            "currency": "USD",
            "mode": "fixed",
            "amount": "0.01"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "parameters": [
          {
            "name": "text",
            "in": "query",
            "required": true,
            "description": "The MSHA data or feature you wish this API had. Plain text, max 1000 characters.",
            "schema": {
              "type": "string",
              "maxLength": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Stored; returns the new request's id.",
            "content": {
              "application/json": {}
            }
          },
          "400": {
            "description": "Empty or over-long text (never charged)."
          },
          "402": {
            "description": "Payment required."
          }
        }
      }
    },
    "/api/x402/feature-request/{id}/upvote": {
      "get": {
        "summary": "Paid ($0.01) upvote of an existing feature request.",
        "description": "x402-gated. Increments the request's upvote count; one counted vote per payer wallet (a repeat vote returns the current count without double-counting). Find ids on the free /api/x402/feature-requests list.",
        "operationId": "upvoteX402FeatureRequest",
        "x-payment-info": {
          "price": {
            "currency": "USD",
            "mode": "fixed",
            "amount": "0.01"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Numeric feature-request id from /api/x402/feature-requests.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Upvote result with the current count.",
            "content": {
              "application/json": {}
            }
          },
          "402": {
            "description": "Payment required."
          },
          "404": {
            "description": "Unknown id (never charged)."
          }
        }
      }
    }
  }
}
