openapi: 3.1.0 info: title: SolarInstallers.io Solar Decision Engine API version: 0.1.0-draft description: > Specification of the SolarInstallers.io solar decision engine. These operations are currently implemented as a client-side JavaScript module (window.SI.engine in /assets/data.js); the same contracts are intended for a hosted REST API and an MCP / WebMCP interface. Endpoints are NOT hosted yet — this document is the stable contract, not a live service. All monetary values are USD. All homeowner figures are planning estimates, not quotes or guarantees. contact: name: SolarInstallers.io email: hello@solarinstallers.io license: name: See site terms url: https://solarinstallers.io/terms.html servers: - url: https://solarinstallers.io/api/v0 description: Planned (not yet live) tags: - name: analysis description: Sizing, economics and financing calculations for a home solar system. - name: incentives description: Structured federal, state and utility incentive records. - name: installers description: Installer directory and transparent match-score ranking. - name: quotes description: Side-by-side comparison of installer proposals. security: - ApiKeyAuth: [] paths: /solar-system: post: tags: [analysis] operationId: calculateSolarSystem summary: Size a residential solar system and estimate its economics requestBody: required: true content: application/json: schema: type: object required: [zip] properties: zip: { type: string, example: "75001" } monthly_bill: { type: number, example: 250 } annual_kwh: { type: [number, "null"] } roof_area: { type: [number, "null"], description: usable roof area in sq ft } roof_orientation: { type: [string, "null"], enum: [South, "East / West", North, Flat, null] } shading: { type: [string, "null"], enum: ["Little to none", Some, Heavy, null] } battery_interest: { type: boolean, default: false } offset: { type: number, default: 1, description: target share of the bill to cover } rate_escalation: { type: number, default: 0.03 } responses: "200": description: OK content: application/json: schema: { $ref: "#/components/schemas/SolarSystemResult" } "4XX": $ref: "#/components/responses/ClientError" /financing: post: tags: [analysis] operationId: calculateSolarFinancing summary: Amortize a solar loan and estimate payback requestBody: required: true content: application/json: schema: type: object required: [system_cost] properties: system_cost: { type: number } down_payment: { type: number, default: 0 } apr: { type: number, default: 0.069 } term: { type: integer, default: 20 } annual_savings: { type: [number, "null"] } responses: "200": description: OK content: application/json: schema: { $ref: "#/components/schemas/FinancingResult" } "4XX": $ref: "#/components/responses/ClientError" /incentives: get: tags: [incentives] operationId: getSolarIncentives summary: Structured incentive records for a location parameters: - { in: query, name: state, schema: { type: string }, example: TX } - { in: query, name: zip, schema: { type: string } } - { in: query, name: technology, schema: { type: string, enum: [solar, battery] } } responses: "200": description: OK content: application/json: schema: type: object properties: state: { type: string } incentives: type: array items: { $ref: "#/components/schemas/Incentive" } note: { type: string } source: { type: string, format: uri } "4XX": $ref: "#/components/responses/ClientError" /installers: get: tags: [installers] operationId: findInstallers summary: Installers ranked by a transparent match score parameters: - { in: query, name: zip, schema: { type: string } } - { in: query, name: state, schema: { type: string } } - { in: query, name: system_size, schema: { type: number } } - { in: query, name: battery, schema: { type: boolean } } - { in: query, name: financing, schema: { type: string } } - { in: query, name: limit, schema: { type: integer } } responses: "200": description: OK content: application/json: schema: type: object properties: state: { type: string } count: { type: integer } weights: type: object additionalProperties: { type: integer } disclosure: { type: string } installers: type: array items: { $ref: "#/components/schemas/InstallerMatch" } "4XX": $ref: "#/components/responses/ClientError" /quote-comparison: post: tags: [quotes] operationId: compareSolarQuotes summary: Compare two or three installer proposals requestBody: required: true content: application/json: schema: type: object required: [quotes] properties: quotes: type: array minItems: 2 maxItems: 3 items: type: object required: [system_size, price] properties: label: { type: string } system_size: { type: number } price: { type: number } ppw: { type: [number, "null"] } battery_kwh: { type: [number, "null"] } warranty_years: { type: [number, "null"] } apr: { type: [number, "null"] } monthly_payment: { type: [number, "null"] } production: { type: [number, "null"] } responses: "200": description: OK content: application/json: schema: type: object properties: best_price: { type: string } best_value: { type: string } best_financing: { type: [string, "null"] } best_warranty: { type: [string, "null"] } best_production: { type: string } overall_recommendation: { type: string } rationale: type: object additionalProperties: { type: string } "4XX": $ref: "#/components/responses/ClientError" components: securitySchemes: ApiKeyAuth: type: apiKey in: header name: X-API-Key description: Planned. No key is required today (the engine runs client-side). responses: ClientError: description: Invalid or missing parameters content: application/json: schema: type: object properties: error: { type: string } schemas: Assumptions: type: array items: { type: string } description: Human-readable list of every assumption behind the numbers SolarSystemResult: type: object properties: state: { type: string } state_name: { type: string } system_size: { type: number, description: kW DC } production: { type: integer, description: year-1 kWh } annual_consumption: { type: integer } coverage_pct: { type: integer } battery_kwh: { type: number } gross_cost: { type: integer } incentives_total: { type: integer } net_cost: { type: integer } annual_savings: { type: integer } monthly_savings: { type: integer } payback: { type: number, description: years } savings_10yr: { type: integer } savings_20yr: { type: integer } savings_25yr: { type: integer } roi_pct: { type: integer } monthly_loan: { type: integer } assumptions: { $ref: "#/components/schemas/Assumptions" } FinancingResult: type: object properties: loan_amount: { type: integer } apr: { type: number } term: { type: integer } monthly_payment: { type: integer } total_interest: { type: integer } total_cost: { type: integer } net_monthly_impact: { type: [integer, "null"] } payback: { type: [number, "null"] } assumptions: { $ref: "#/components/schemas/Assumptions" } Incentive: type: object properties: id: { type: string } name: { type: string } level: { type: string, enum: [federal, state, utility, local] } jurisdiction: { type: string } technologies: { type: array, items: { type: string } } eligibility: { type: string } value: { type: string } value_type: { type: string, enum: [tax_credit, rebate, performance, exemption] } expiration: { type: [string, "null"], format: date } source_name: { type: string } source_url: { type: string, format: uri } last_verified: { type: string, format: date } InstallerMatch: type: object properties: slug: { type: string } name: { type: string } website: { type: string, format: uri } hq: { type: string } founded: { type: integer } profile_url: { type: string } match_score: { type: integer, minimum: 0, maximum: 100 } match_breakdown: type: object additionalProperties: type: object properties: weight: { type: integer } factor: { type: number } points: { type: number }