{
  "openapi": "3.1.0",
  "info": {
    "title": "Salvage Signal Public API",
    "version": "1.3.0",
    "description": "Read-only Chicago demolition-permit signals, bounded permit and address lookup, a current teardown-to-reuse brief, canonical product facts, and a human-confirmed subscription handoff. Public permits are leads, not access permission.",
    "contact": {
      "email": "hello@salvagesignal.com",
      "url": "https://salvagesignal.com"
    }
  },
  "servers": [
    {
      "url": "https://salvagesignal.com"
    }
  ],
  "x-mcp-server": {
    "transport": "streamable-http",
    "protocolVersion": "2025-11-25",
    "endpoint": "https://salvagesignal.com/api/mcp",
    "manifest": "https://salvagesignal.com/mcp.json",
    "officialRegistryName": "com.salvagesignal/salvage-signal",
    "officialRegistryListing": "https://registry.modelcontextprotocol.io/?search=salvage",
    "authenticationRequired": false,
    "access": "Public read-only data only"
  },
  "paths": {
    "/api/signals": {
      "get": {
        "operationId": "getChicagoSalvageSignals",
        "summary": "Get ranked Chicago demolition signals",
        "description": "Returns public demolition permits ranked by explainable architectural-salvage and material-reuse clues.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "default": 10
            }
          },
          {
            "name": "include_interior",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ranked public permit signals",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignalResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/signals.csv": {
      "get": {
        "operationId": "downloadChicagoSalvageSignalsCsv",
        "summary": "Download the current ranked Chicago signal window as CSV",
        "description": "Returns a public flat-file distribution with source-linked permit facts and explainable Salvage Signal ranking fields.",
        "responses": {
          "200": {
            "description": "Current ranked Chicago signals as CSV",
            "content": { "text/csv": { "schema": { "type": "string" } } }
          }
        }
      }
    },
    "/api/permit-lookup": {
      "get": {
        "operationId": "lookupChicagoDemolitionPermit",
        "summary": "Look up a Chicago demolition permit",
        "description": "Runs a bounded read-only lookup by one permit number or one Chicago street address. Arbitrary database query syntax is rejected.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "One City permit number or one Chicago street address.",
            "schema": { "type": "string", "minLength": 2, "maxLength": 80 },
            "examples": { "permit": { "value": "101084286" }, "address": { "value": "911 W Diversey Pkwy" } }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching demolition permits and explainable salvage scores",
            "content": { "application/json": { "schema": { "type": "object" } } }
          },
          "400": { "description": "Blank or malformed lookup" },
          "503": { "description": "Permit lookup temporarily unavailable" }
        }
      }
    },
    "/api/brief": {
      "get": {
        "operationId": "getChicagoTeardownToReuseBrief",
        "summary": "Get the current Chicago Teardown-to-Reuse Brief",
        "description": "Returns aggregate signal counts and an eight-item watch list with indexable detail URLs.",
        "responses": {
          "200": {
            "description": "Current public brief",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/discovery": {
      "get": {
        "operationId": "getSalvageSignalProduct",
        "summary": "Get canonical product and offer facts",
        "responses": {
          "200": {
            "description": "Product discovery record",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/agent-checkout": {
      "get": {
        "operationId": "getSalvageSignalSubscriptionHandoff",
        "summary": "Prepare a human-confirmed subscription handoff",
        "description": "Returns exact Chicago Daily Drop terms, live aggregate founding availability, agent limits, and attributed preview and signup URLs. This operation is read-only, accepts no contact or payment data, and cannot complete a purchase.",
        "parameters": [
          {
            "name": "source",
            "in": "query",
            "required": false,
            "description": "Short agent or channel identifier used only for signup attribution.",
            "schema": { "type": "string", "maxLength": 64, "default": "agent" }
          },
          {
            "name": "campaign",
            "in": "query",
            "required": false,
            "description": "Short campaign identifier used only for signup attribution.",
            "schema": { "type": "string", "maxLength": 64, "default": "subscription_handoff" }
          }
        ],
        "responses": {
          "200": {
            "description": "Read-only handoff requiring human confirmation and hosted Stripe Checkout",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SubscriptionHandoff" }
              }
            }
          }
        }
      }
    },
    "/api/mcp": {
      "post": {
        "operationId": "callSalvageSignalMcp",
        "summary": "Call the public read-only Salvage Signal MCP server",
        "description": "Stateless Streamable HTTP MCP endpoint. Supports initialize, ping, tools/list, and tools/call over JSON-RPC 2.0. See /mcp.json for discovery facts.",
        "parameters": [
          {
            "name": "MCP-Protocol-Version",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "enum": ["2025-11-25", "2025-06-18", "2025-03-26"]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["jsonrpc", "method"],
                "properties": {
                  "jsonrpc": { "const": "2.0" },
                  "id": { "type": ["string", "number", "null"] },
                  "method": { "type": "string" },
                  "params": { "type": "object" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response",
            "content": {
              "application/json": {
                "schema": { "type": "object" }
              }
            }
          },
          "202": { "description": "Notification accepted" },
          "400": { "description": "Invalid JSON-RPC or MCP protocol version" },
          "403": { "description": "Forbidden browser origin" },
          "415": { "description": "Content-Type must be application/json" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Signal": {
        "type": "object",
        "required": [
          "id",
          "address",
          "salvageScore",
          "urgency",
          "likelyInventory",
          "rankingReasons"
        ],
        "properties": {
          "id": { "type": "string" },
          "permitNumber": { "type": ["string", "null"] },
          "issuedAt": { "type": ["string", "null"] },
          "address": { "type": "string" },
          "scope": { "type": ["string", "null"] },
          "reportedCostUsd": { "type": ["number", "null"] },
          "publicContact": { "type": ["string", "null"] },
          "latitude": { "type": ["number", "null"] },
          "longitude": { "type": ["number", "null"] },
          "salvageScore": { "type": "integer", "minimum": 0, "maximum": 100 },
          "urgency": { "type": "string", "enum": ["HOT", "WARM", "WATCH"] },
          "likelyInventory": { "type": "array", "items": { "type": "string" } },
          "rankingReasons": { "type": "array", "items": { "type": "string" } }
        }
      },
      "SignalResponse": {
        "type": "object",
        "required": [
          "schemaVersion",
          "generatedAt",
          "market",
          "dataMode",
          "source",
          "safety",
          "signalCount",
          "signals"
        ],
        "properties": {
          "schemaVersion": { "type": "string" },
          "generatedAt": { "type": "string", "format": "date-time" },
          "market": { "type": "string" },
          "dataMode": { "type": "string", "enum": ["live", "verified_sample"] },
          "source": { "type": "string" },
          "sourceUrl": { "type": "string", "format": "uri" },
          "methodologyUrl": { "type": "string", "format": "uri" },
          "safety": { "type": "string" },
          "signalCount": { "type": "integer" },
          "signals": { "type": "array", "items": { "$ref": "#/components/schemas/Signal" } },
          "paidDailyDrop": { "type": "string", "format": "uri" }
        }
      },
      "FoundingOfferAvailability": {
        "type": "object",
        "required": ["status", "capacity", "paying", "remaining", "checkedAt"],
        "properties": {
          "status": { "type": "string", "enum": ["available", "filled", "unknown"] },
          "capacity": { "type": "integer", "const": 10 },
          "paying": { "type": ["integer", "null"], "minimum": 0 },
          "remaining": { "type": ["integer", "null"], "minimum": 0, "maximum": 10 },
          "checkedAt": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "SubscriptionPricing": {
        "type": "object",
        "required": [
          "currency",
          "foundingFirstMonth",
          "recurringMonthly",
          "billingInterval",
          "foundingOffer",
          "minimumTermMonths",
          "cancellation"
        ],
        "properties": {
          "currency": { "const": "USD" },
          "foundingFirstMonth": { "type": "number", "const": 19 },
          "recurringMonthly": { "type": "number", "const": 39 },
          "billingInterval": { "const": "month" },
          "foundingOffer": { "$ref": "#/components/schemas/FoundingOfferAvailability" },
          "minimumTermMonths": { "type": "integer", "const": 0 },
          "cancellation": { "type": "string" }
        }
      },
      "SubscriptionHandoff": {
        "type": "object",
        "required": [
          "schemaVersion",
          "type",
          "status",
          "readOnly",
          "product",
          "pricing",
          "humanDecision",
          "agentPolicy",
          "links"
        ],
        "properties": {
          "schemaVersion": { "type": "string" },
          "type": { "const": "subscription_handoff" },
          "status": { "const": "human_confirmation_required" },
          "readOnly": { "const": true },
          "product": { "type": "object" },
          "pricing": { "$ref": "#/components/schemas/SubscriptionPricing" },
          "humanDecision": { "type": "object" },
          "agentPolicy": { "type": "object" },
          "links": { "type": "object" },
          "attribution": { "type": "object" },
          "support": { "type": "string", "format": "email" }
        }
      }
    }
  }
}
