openapi: 3.0.0 info: title: Xpansiv Managed Solutions API description: >- Access data from your Xpansiv Managed Solutions account using API calls. You can generate an API key for your user on Xpansiv Managed Solutions API Access page. The API key is linked to a user and an account, and has the same rights as the user on the account. When calling Xpansiv Managed Solutions API use that API key to set the Bearer Token authentication header. contact: email: developers@xpansiv.com version: '1.10' servers: - url: https://www.ms.xpansiv.com/app/api/v1 paths: /facilities/{facility_id}: get: tags: - Facilities summary: Facility description: >- Retrieve detailed information about a facility by its ID, including owner, installer, and certification data. operationId: getFacilityById parameters: - name: facility_id in: path description: ID of the facility to retrieve required: true schema: type: integer responses: '200': description: Facility data retrieved successfully content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/FacilityResponse' type: object '400': description: Invalid facility ID provided or facility not found content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/FacilityGetMissingIdError' type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '404': description: Facility not found content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 404 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/FacilityGetError' type: object '500': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ServerError' type: object patch: tags: - Facilities summary: Update facility description: >- Partially updates an existing facility via `PATCH /api/v1/facilities/{facility_id}`. Send only the fields to change. The request body accepts: `facilityCreateOrUpdate`, `facilityDggCreateOrUpdate`, `facilityAbpCreateOrUpdate`, or the legacy flat formats `facilityFlatCreateOrUpdate`, `facilityDggFlatCreateOrUpdate`, and `facilityAbpFlatCreateOrUpdate`. - **Partial mode:** send query parameter `partial=true` to relax required-field validation. In partial mode, required-field validation is relaxed while format/type validation still applies. operationId: updateFacility parameters: - name: facility_id in: path description: ID of the facility to update required: true schema: type: integer - name: partial in: query description: Enable update with partial data. Accepts `true`, `1`, or `yes`. required: false schema: type: boolean default: false requestBody: required: true content: application/json: schema: oneOf: - $ref: '#/components/schemas/facilityCreateOrUpdate' - $ref: '#/components/schemas/facilityDggCreateOrUpdate' - $ref: '#/components/schemas/facilityAbpCreateOrUpdate' - $ref: '#/components/schemas/facilityFlatCreateOrUpdate' - $ref: '#/components/schemas/facilityDggFlatCreateOrUpdate' - $ref: '#/components/schemas/facilityAbpFlatCreateOrUpdate' responses: '200': description: Facility updated successfully content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: >- #/components/schemas/facilityFlatCreateOrUpdateResponse200 type: object '400': description: >- Bad request — validation errors, invalid facility ID, or update failure content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ValidationError' type: object '401': description: Unauthorized — missing API key or invalid token content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '403': description: Facility not visible to the authenticated account content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AccessForbiddenError' type: object '500': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ServerError' type: object /facilities: get: tags: - Facilities summary: Facilities description: >- Retrieve the list of facilities in your Xpansiv Managed Solutions account with pagination. operationId: getFacilityListForAccount parameters: - name: account_id in: query description: >- Account whose facilities to list. Defaults to the authenticated user's active account. **Admin only** when targeting another account; non-admins receive 403 if set to a different account. required: false schema: type: integer example: 4242 minimum: 1 - name: per_page in: query description: >- Number of facilities to display per page. If a value greater than 1000 is provided, it will be coerced to 1000. required: false schema: type: integer default: 50 maximum: 1000 minimum: 1 - name: page in: query description: The current page number required: false schema: type: integer default: 1 minimum: 1 - name: program in: query description: Narrows the list to facilities in a program. required: false schema: type: string example: dgg enum: - dgg - name: search in: query description: Search term matched against facility name required: false schema: type: string example: solar - name: utility_ids in: query description: Comma-separated utility IDs to filter by required: false schema: type: string example: 12,34 - name: auto_reporter_ids in: query description: >- Comma-separated auto-reporter (remote data collector) IDs to filter by required: false schema: type: string example: 5,6 - name: commencement_date_from in: query description: >- Filter by facility commencement date on or after this day (YYYY-MM-DD) required: false schema: type: string format: date example: '2024-01-01' - name: commencement_date_to in: query description: >- Filter by facility commencement date on or before this day (YYYY-MM-DD) required: false schema: type: string format: date example: '2025-12-31' - name: exclude_managed_facility_group_members in: query description: >- When true, omit facilities that belong to a managed facility group owned by the listed account. required: false schema: type: boolean example: true - name: ticket_owner_ids in: query description: Comma-separated ticket owner user IDs to filter by required: false schema: type: string example: 125,126 responses: '200': description: Successfully retrieved facilities content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/PaginatedResponseEnvelope' - properties: code: type: integer example: 200 type: object - $ref: '#/components/schemas/FacilitiesListForAccountList' '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '403': description: Forbidden — non-admin requested another account's facilities content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/Error' type: object post: tags: - Facilities summary: Create facility description: >- Creates a new facility for your account via `POST /api/v1/facilities`. The JSON body must be one of: `facilityCreateOrUpdate` for **Standard Facility** : grouped payload using `address`, `owner`, `installer`, and `meter` objects. Optional `panels` and `inverters` arrays. `facilityDggCreateOrUpdate` for **California Distributed Generation Program** (DGG). - Send `dgg: true` and `state: 'CA'`. - **Metering vs QRE:** `can_provide_solar_production_data_access` is required—when **true**, send `meter_remote_data_collector` and `online_monitoring_api_id`; when **false**, send `qualified_reporting_entity` and `qualified_reporting_entity_id_value`. - Program access: the DGG program is not open to all users; to join, contact our support team. `facilityAbpCreateOrUpdate` for **Illinois Adjustable Block Program** (ABP). - Send `address.state: 'IL'`. Your account must be ABP-enabled. - Include ABP-specific fields (see schema), `panels[].bifacial`, `inverters[].efficiency`, and `installer_demographics`. - After create, call `GET /facilities/{id}/state_eligibilities` for required document slugs before `POST /facilities/{id}/documents`. - Program access: Illinois ABP via API is limited to authorized accounts; contact our support team to enroll. **Partial mode:** send query parameter `partial=true` to create a facility with partial data. In partial mode, required-field validation is relaxed while format/type validation still applies. Legacy flat formats `facilityFlatCreateOrUpdate`, `facilityDggFlatCreateOrUpdate`, and `facilityAbpFlatCreateOrUpdate` remain supported for backwards compatibility and will be deprecated in a future release. This API endpoint only support Solar Photovoltaic facilities. operationId: createFacility parameters: - name: partial in: query description: >- Enable creation of facility with partial data. Accepts `true`, `1`, or `yes`. required: false schema: type: boolean default: false requestBody: required: false content: application/json: schema: oneOf: - $ref: '#/components/schemas/facilityCreateOrUpdate' - $ref: '#/components/schemas/facilityDggCreateOrUpdate' - $ref: '#/components/schemas/facilityAbpCreateOrUpdate' - $ref: '#/components/schemas/facilityFlatCreateOrUpdate' - $ref: '#/components/schemas/facilityDggFlatCreateOrUpdate' - $ref: '#/components/schemas/facilityAbpFlatCreateOrUpdate' responses: '200': description: Facility created successfully content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: >- #/components/schemas/facilityFlatCreateOrUpdateResponse200 type: object '400': description: Bad request - authorization or validation errors content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ValidationError' type: object '401': description: >- Unauthorized — missing API key, invalid token, or user lacks facility edit permission content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '403': description: Access denied for creating a DGG facility content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AccessForbiddenError' type: object '500': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ServerError' type: object /facilities/{facility_id}/transactions: get: tags: - Transactions summary: Get Transaction Summary/History description: Returns transaction summary/history for a facility. operationId: getTransactionsHistory parameters: - name: facility_id in: path description: ID of the facility to retrieve transactions for required: true schema: type: integer - name: per_page in: query description: Number of transactions to display per page. Maximum 1000. required: false schema: type: integer default: 50 maximum: 1000 minimum: 1 - name: page in: query description: The current page number required: false schema: type: integer default: 1 minimum: 1 responses: '200': description: Transaction summary/history retrieved successfully content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/PaginatedResponseEnvelope' - properties: code: type: integer example: 200 type: object - $ref: '#/components/schemas/FacilityTransactionsPaginatedResponse' '400': description: Bad request - invalid facility ID format content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: >- #/components/schemas/TransactionsInvalidFacilityIdError type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '403': description: Access denied or facility closed/lost content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/TransactionsForbiddenError' type: object '404': description: Facility not found content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 404 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/TransactionsNotFoundError' type: object /facilities/{facility_id}/state_eligibilities: get: tags: - Facilities summary: State Eligibilities description: >- Retrieve state eligibility and required documents for a facility. For Illinois ABP, use the returned `documents[].upload_field_name` values as `document_name` when calling `POST /facilities/{id}/documents`. Required documents depend on facility attributes (e.g. `net_metering_approved`); always use this endpoint rather than a static slug list. operationId: getFacilityStateEligibilities parameters: - name: facility_id in: path description: The ID of the facility to fetch eligibility details for. required: true schema: type: integer example: 130240 responses: '200': description: Successful response with state eligibility details. content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/FacilityStateEligibilitiesList' type: object '400': description: Bad Request - The request was invalid or cannot be otherwise served. content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/FacilityGetMissingIdError' type: object '404': description: Not Found - The requested facility was not found. content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 404 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/FacilityGetError' type: object /facilities/{facility_id}/state_applications: post: tags: - Facilities summary: State Application description: Apply to a new state to be certified to generate RECs. operationId: createFacilityStateApplication parameters: - name: facility_id in: path description: The ID of the facility linked to the new application. required: true schema: type: integer example: 130240 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateFacilityStateApplicationRequest' responses: '200': description: State application created successfully for the given state content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/StateApplicationCreateData' type: object '400': description: Bad request - authorization or validation errors content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ValidationError' type: object '500': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ServerError' type: object /facilities/{facility_id}/documents: post: tags: - Facilities summary: Facilities Documents description: >- Upload documents or photos for a facility (multipart form). Call `GET /facilities/{facility_id}/state_eligibilities` first to obtain each document's `upload_field_name` slug. For Illinois ABP, common slugs include `proof_of_irrevocable_transfer`, `proof_of_residential`, and `proof_of_site_control`; the eligibility response is authoritative. operationId: uploadFacilityDocuments parameters: - name: facility_id in: path description: The ID of the facility required: true schema: type: string requestBody: required: true content: multipart/form-data: schema: $ref: '#/components/schemas/UploadFacilityDocumentsRequest' responses: '200': description: Documents uploaded successfully content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/uploadFacilityDocumentsResponse' type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object /facilities/{facility_id}/state_certifications: post: tags: - Facilities summary: State Certifications description: >- Add state certificates to a facility. This endpoint is accessible for certain user only. If you want to use it, please contact Xpansiv Managed Solutions. operationId: addFacilityStateCertification parameters: - name: facility_id in: path description: The ID of the facility linked to the state certification. required: true schema: type: integer example: 130240 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AddFacilityStateCertificationRequest' responses: '200': description: State certification created successfully for the given state content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/StateCertificationCreateData' type: object '400': description: Bad request - authorization or validation errors content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ValidationError' type: object '500': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ServerError' type: object /facilities/{facility_id}/validate: get: tags: - Facilities summary: Validate facility description: >- Runs the same validation checks as facility completion (required fields, program eligibility, and missing documents) without changing facility state. Success and error payloads match `POST /facilities/{facility_id}/complete`. Use to inspect issues before calling complete. operationId: validateFacility parameters: - name: facility_id in: path description: The ID of the facility to validate. required: true schema: type: integer example: 130240 responses: '200': description: >- Facility passes the same checks as completion (or is already completed) content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/FacilityCompleteSuccess' type: object '400': description: >- Bad request — missing facility_id, facility not found, closed-lost, or missing documents content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/FacilityCompleteError' type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '403': description: Facility not visible to the authenticated account content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/FacilityGetError' type: object '404': description: Bad request URL or resource not found content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 404 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/Error' type: object '422': description: >- Unprocessable — validation failed, no eligible states, or fee-bearing states content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 422 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/FacilityCompleteError' type: object /facilities/{facility_id}/complete: post: tags: - Facilities summary: Complete facility application description: >- Validate and complete a facility application via `POST /api/v1/facilities/{facility_id}/complete`. **Prerequisites:** create or update the facility (`POST` / `PATCH /facilities`), then call `GET /facilities/{facility_id}/state_eligibilities` to determine eligible states and required document slugs, and upload documents via `POST /facilities/{facility_id}/documents`. - **`states`:** optional. When omitted or empty, all eligible zero-fee (or fee-waived) states are completed. When provided, only those states are completed. - **Fees:** only zero-fee or fee-waived states can be completed via the API. States with a fee must be completed through the web checkout flow. - **`agree_to_terms_and_conditions`:** Must be `true`. By setting this field to `true` you agree to the following terms and conditions: > I, (Name), hereby consent to SRECTrade Inc, dba Xpansiv Managed Solutions serving as my Agent in the Registration of "[Facility Name]" ("Generation Unit") as a qualified renewable energy generator, within a state's program that may provide an opportunity to sell renewable energy certificates (RECs), or other environmental attributes, from the Generation Unit during its eligibility period, and for providing additional and updated information to the appropriate entities as may be required during and after the certification and registration process. Furthermore, if I am not the rightful owner of the Generation Unit and/or the RECs, or other environmental attributes, I affirm that I have been granted permission by the owner(s) to act as the Applicant in order to submit an application for the Generation Unit with SRECTrade Inc, dba Xpansiv Managed Solutions and to act as the Representative in agreeing to the outlined Terms and Conditions. > I affirm that, to the best of my knowledge, the information that I provided in this application is complete and accurate. I agree that, should any changes be made to the information provided herein, I will contact SRECTrade Inc, dba Xpansiv Managed Solutions to notify it of such changes so that it may inform the appropriate entities. I acknowledge that failure to do so may impact the eligibility of the Generation Unit and its ability to generate RECs, or other environmental attributes. I understand that SRECTrade Inc, dba Xpansiv Managed Solutions assumes no liability for such impacts to the Generation Unit's eligibility. I understand that SRECTrade Inc, dba Xpansiv Managed Solutions does not guarantee nor warrant same day submission of the application to the state's program, which may impact the certification start date. I understand that the application process with the state's program is contingent on the program's timeline. > By my consent, I knowingly and freely agree to assume all the risks, both known and unknown, surrounding the Generation Unit's participation in the SREC, REC, or other environmental attribute programs as a qualified renewable energy generator. In recognition of the relative risks and benefits of this arrangement between myself ("Applicant") and SRECTrade Inc, dba Xpansiv Managed Solutions, the risks have been allocated such that I agree, to the fullest extent permitted by law, to limit the liability of SRECTrade Inc, dba Xpansiv Managed Solutions to the Applicant or to any third party for any and all claims, losses, costs, damages of any nature whatsoever or claims expenses from any cause or causes, including attorneys' fees and costs and expert witness fees and costs, so that the total aggregate liability SRECTrade Inc, dba Xpansiv Managed Solutions has to the Applicant or to any third party shall not exceed the greater of (a) the total fees I paid to SRECTrade Inc, dba Xpansiv Managed Solutions in the twelve (12) months prior to the action or transaction giving rise to the liability, and (b) U.S. $100.00. On success, missing state applications are created and the facility is submitted to Xpansiv Managed Solutions for review. operationId: completeFacility parameters: - name: facility_id in: path description: The ID of the facility to complete. required: true schema: type: integer example: 130240 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CompleteFacilityRequest' responses: '200': description: Facility application completed successfully content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/FacilityCompleteSuccess' type: object '400': description: >- Bad request — invalid `states` format, missing terms agreement, invalid facility, or missing documents content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/FacilityCompleteError' type: object '401': description: >- Unauthorized — missing API key, invalid token, or user lacks facility edit permission content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '403': description: Facility not visible to the authenticated account content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/FacilityGetError' type: object '404': description: Bad request URL or resource not found content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 404 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/Error' type: object '422': description: >- Unprocessable — facility already completed, validation failed, no eligible states, or fee-bearing states content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 422 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/FacilityCompleteError' type: object '500': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ServerError' type: object /facilities/{facility_id}/generation/history: get: tags: - Transactions summary: Get Production History description: Returns production history for a facility. operationId: getProductionHistory parameters: - name: facility_id in: path required: true schema: type: integer responses: '200': description: Production history retrieved successfully content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/PaginatedResponseEnvelope' - properties: code: type: integer example: 200 type: object - $ref: '#/components/schemas/ProductionHistoryPaginatedResponse' '400': description: Bad request — generation history could not be loaded content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ProductionHistoryBadRequestError' type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '404': description: Facility not found content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 404 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/FacilityGetError' type: object /uploads/{upload_id}/errors/{row_id}/preview/{error_type}/{field}: get: tags: - Facility import error handling summary: Preview fix for one upload row’s error (same error group context) description: >- Like bulk preview, but scoped to a single row_id within the upload. fix_value is optional and passed to the fixer. Requires access to the upload (facilities, ABP, or DGG). operationId: facilityUploadSingleRowErrorPreview parameters: - name: upload_id in: path required: true schema: type: integer minimum: 1 - name: row_id in: path description: Upload row identifier required: true schema: type: integer minimum: 1 - name: error_type in: path required: true schema: type: string - name: field in: path required: true schema: type: string - name: fix_value in: query description: Optional proposed value for the fixer required: false schema: type: string - name: error_index in: query description: >- When multiple errors exist on the row for this error group, selects which error instance to preview (0-based). Invalid values return 400. required: false schema: type: integer responses: '200': description: Preview available content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: >- #/components/schemas/UploadSingleRowErrorPreviewSuccess type: object '400': description: Invalid error_index query parameter content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: >- #/components/schemas/UploadErrorPreviewInvalidErrorIndexError type: object '403': description: No access to this upload content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/UploadErrorPreviewForbiddenError' type: object '404': description: Error group or fixer not found content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 404 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/UploadErrorPreviewNotFoundError' type: object /uploads/{upload_id}/errors/preview/{error_type}/{field}: get: tags: - Facility import error handling summary: Preview bulk fix for an upload error group description: >- Returns a preview of proposed changes for all rows in the error group identified by error_type and field. Optional query parameter fix_value is passed to the fixer. Caller must have access to the upload (facilities, ABP, or DGG). operationId: facilityUploadErrorPreviewBulk parameters: - name: upload_id in: path required: true schema: type: integer minimum: 1 - name: error_type in: path required: true schema: type: string - name: field in: path required: true schema: type: string - name: fix_value in: query description: Optional value passed to the fixer (e.g. replacement text). required: false schema: type: string responses: '200': description: Preview succeeded content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/UploadBulkErrorPreviewSuccess' type: object '403': description: No permission to access this upload content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/UploadErrorPreviewForbiddenError' type: object '404': description: No matching error group or fixer content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 404 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/UploadErrorPreviewNotFoundError' type: object /uploads/{upload_id}/errors/{row_id}/fix: post: tags: - Facility import error handling summary: Apply error fix for one upload row description: Facility import error handling operationId: facilityUploadApplyFixSingleRow parameters: - name: upload_id in: path required: true schema: type: integer minimum: 1 - name: row_id in: path required: true schema: type: integer minimum: 1 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ApplyFixRequestBody' responses: '200': description: Fix applied content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/SingleFixSuccessResponse' type: object '400': description: Missing fields, fix/validation failed content: application/json: schema: oneOf: - $ref: '#/components/schemas/ApplyFixValidationErrorResponse' - $ref: '#/components/schemas/ApplyFixFieldErrorResponse' examples: validation_failed_all_rows: summary: Fix applied but validation failed (per-row errors) value: success: false error: Validation failed validation: '7050956': error: >- The facility_size 30.2222kW is different than the total panels capacity 28.615kW (DC). attempted_value: '30.2222' '7050957': error: >- The facility_size 30.2222kW is different than the total panels capacity 29.585kW (DC). attempted_value: '30.2222' facility_creation_attempt: [] missing_params: summary: Fix applied but validation failed (per-row errors) value: success: false error: No correction value provided validation: {} facility_creation_attempt: [] invalid_error_index: summary: error_index in body is not a valid integer value: success: false error: Invalid error_index value. '403': description: No access to upload content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ForbiddenResponse' type: object /uploads/{upload_id}/errors/fix: post: tags: - Facility import error handling summary: Apply error fix for all rows in the error group description: Facility import error handling operationId: facilityUploadApplyFixBulk parameters: - name: upload_id in: path required: true schema: type: integer minimum: 1 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ApplyFixRequestBody' responses: '200': description: Bulk fix applied content: application/json: schema: $ref: '#/components/schemas/BulkFixSuccessResponse' examples: mixed_create_outcomes: summary: Validation OK; some facilities created, some errors value: success: true facility_creation_attempt: '7050928': status: success message: Facility created successfully facility_id: 184869 '7050929': status: error message: A facility with this address already exists. '7050930': status: error message: A facility with this address already exists. validation: [] validation_fine_but_facility_can_not_be_created: summary: Validation OK; facility can not be created value: success: true facility_creation_attempt: '7050929': status: error message: A facility with this address already exists. validation: [] all_rows_success: summary: Every row created a facility value: success: true facility_creation_attempt: '7050928': status: success message: Facility created successfully facility_id: 184869 '7050929': status: success message: Facility created successfully facility_id: 184870 validation: [] partial_success_pending_and_validation_errors: summary: One row pending (more errors); another row failed validation value: success: true facility_creation_attempt: '7050956': status: pending message: Facility candidate has more error to resolve validation: '7050957': error: >- The facility_size 28.615kW is different than the total panels capacity 29.585kW (DC). attempted_value: '28.615' partial_success_create_and_validation_errors: summary: One row created a facility; another row failed validation value: success: true facility_creation_attempt: '7050956': status: success message: Facility created successfully facility_id: 184869 validation: '7050957': error: >- The facility_size 28.615kW is different than the total panels capacity 29.585kW (DC). attempted_value: '28.615' '400': description: Missing fields or fix/validation failed content: application/json: schema: oneOf: - $ref: '#/components/schemas/ApplyFixValidationErrorResponse' - $ref: '#/components/schemas/ApplyFixFieldErrorResponse' examples: validation_failed_all_rows: summary: Fix applied but validation failed (per-row errors) value: success: false error: Validation failed validation: '7050956': error: >- The facility_size 30.2222kW is different than the total panels capacity 28.615kW (DC). attempted_value: '30.2222' '7050957': error: >- The facility_size 30.2222kW is different than the total panels capacity 29.585kW (DC). attempted_value: '30.2222' facility_creation_attempt: [] missing_params: summary: Fix applied but validation failed (per-row errors) value: success: false error: No correction value provided validation: {} facility_creation_attempt: [] '403': description: No access to upload content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ForbiddenResponse' type: object /facilities/{facility_id}/generation/meters/{meter_id}/readings/eligibility: get: tags: - Meter Readings summary: Check user can submit meter reading description: >- Check if the facility and meter are eligible for manual meter reading submissions. operationId: userCanSubmitMeterReading parameters: - name: facility_id in: path description: ID of the facility to check eligibility for required: true schema: type: integer - name: meter_id in: path description: ID of the meter to check eligibility for required: true schema: type: integer responses: '200': description: Eligibility check completed successfully content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/MeterReadingEligibilityResult' type: object '400': description: Bad request - invalid facility or meter ID content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: >- #/components/schemas/MeterReadingEligibilityBadRequestError type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '500': description: Internal server error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: >- #/components/schemas/MeterReadingEligibilityServerError type: object /facilities/{facility_id}/generation/meters/{meter_id}/readings: post: tags: - Meter Readings summary: Submit Meter Reading (Cumulative Meters Only) description: >- Submit a meter reading for a facility with a **cumulative meter only**. **Important**: This endpoint only accepts submissions for cumulative meters. Non-cumulative meters will be rejected with error code `CUMULATIVE_ONLY`. Supports both manual and automated submissions: - **Manual submission**: Provide `meter_reading` and `meter_reading_date_taken` (required for cumulative meters) - **Automated submission**: Provide `processing_id` to extract reading from automated processing system **Additional restrictions**: - First-time submissions are not allowed (at least one reading must already exist) - Meter must be verified and eligible for manual reading entry operationId: submitMeterReading parameters: - name: facility_id in: path description: The unique identifier of the facility required: true schema: type: integer example: 12345 minimum: 1 - name: meter_id in: path description: The unique identifier of the cumulative meter required: true schema: type: integer example: 67890 minimum: 1 requestBody: required: true content: application/json: schema: oneOf: - $ref: '#/components/schemas/ManualMeterReadingRequest' - $ref: '#/components/schemas/AutomatedMeterReadingRequest' responses: '200': description: Meter reading submitted successfully content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/MeterReadingSuccessResponse' type: object '400': description: Bad Request - Validation failed or invalid parameters content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/MeterReadingErrorResponse' type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '403': description: Forbidden - User lacks permission to submit readings content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/MeterReadingErrorResponse' type: object '404': description: Not Found - Facility not found content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 404 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/MeterReadingErrorResponse' type: object '410': description: Gone - Facility is inactive content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 410 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/MeterReadingErrorResponse' type: object '500': description: Internal Server Error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/MeterReadingErrorResponse' type: object /facilities/{facility_id}/generation/meters/{meter_id}/image-readings: post: tags: - Meter Readings summary: Process Meter Reading Image description: >- Accepts a meter reading photo and returns a processing ID for status tracking. operationId: processMeterReadingImage parameters: - name: facility_id in: path description: ID of the facility required: true schema: type: integer - name: meter_id in: path description: ID of the meter required: true schema: type: integer requestBody: required: true content: multipart/form-data: schema: $ref: '#/components/schemas/ProcessMeterReadingImageRequest' responses: '200': description: Image accepted for processing content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/ProcessMeterReadingImageSuccess' type: object '400': description: Bad request - validation or file errors content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: >- #/components/schemas/ProcessMeterReadingImageBadRequestError type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '429': description: Too many failed photo submission attempts - manual entry required content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 429 elements: type: integer example: 0 type: object - properties: data: $ref: >- #/components/schemas/ProcessMeterReadingImageMaxAttemptsError type: object '500': description: Internal server error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: >- #/components/schemas/ProcessMeterReadingImageServerError type: object /facilities/{facility_id}/generation/meters/{meter_id}/readings/{meter_readings_id}/verification: post: tags: - Meter Readings summary: Submit Verification for a Held Meter Reading description: >- Upload a photo and a written description to justify a held meter reading (held generation). This endpoint is used when a reading has been flagged as needing user action (status `held_high`, `held_low`, `held_less_than_previous`, or `srectrade_disapproved`). A successful submission changes the reading status to `pending_review`. **Validation rules**: - `facility_id`, `meter_id`, and `meter_readings_id` must be positive integers - The authenticated user's account must have access to the facility - The facility must be active (not closed/lost) - The `meter_id` must be the primary meter of the facility - The reading must exist, belong to the meter, and have a status that requires user action - An image file must be provided (`image` field, `multipart/form-data`) - Accepted image types: `png`, `gif`, `jpg`, `jpeg` (extension and MIME type are both validated) - A non-empty `description` is required (max 512 characters) operationId: submitReadingVerification parameters: - name: facility_id in: path description: The unique identifier of the facility required: true schema: type: integer example: 12345 minimum: 1 - name: meter_id in: path description: The unique identifier of the primary meter of the facility required: true schema: type: integer example: 67890 minimum: 1 - name: meter_readings_id in: path description: The unique identifier of the held meter reading to verify required: true schema: type: integer example: 98765 minimum: 1 requestBody: required: true content: multipart/form-data: schema: required: - image - description properties: image: description: >- Photo justifying the meter reading. Accepted types: png, gif, jpg, jpeg. type: string format: binary description: description: >- Written justification for the held reading (1–512 characters). type: string example: Meter was temporarily obstructed by construction equipment. maxLength: 512 minLength: 1 type: object responses: '201': description: Verification submitted — reading moved to `pending_review` content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 201 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/SubmitReadingVerificationSuccess' type: object '400': description: '`NO_IMAGE` — the `image` field is absent from the request.' content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ApiResponseDTO' type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '403': description: >- Forbidden. Possible `error` codes: - `permission_denied` — user lacks the `REPORT_PRODUCTION` ACL permission - `FACILITY_ACCESS_DENIED` — user's account cannot access this facility - `INACTIVE_FACILITY` — the facility has been closed/lost content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ApiResponseDTO' type: object '404': description: >- Not Found. Possible `error` codes: - `FACILITY_NOT_FOUND` — no facility exists for the given `facility_id` - `READING_NOT_FOUND` — reading does not exist or does not belong to the given meter content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 404 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ApiResponseDTO' type: object '409': description: >- Conflict — `READING_NOT_ELIGIBLE`. The reading exists but its current status does not require user action (e.g. it is already approved, pending review, or fully processed). content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 409 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ApiResponseDTO' type: object '422': description: >- Unprocessable Entity — request is well-formed but data validation failed. Possible `error` codes: - `invalid_parameters` — one or more path IDs are not valid positive integers - `METER_ID_MISMATCH` — `meter_id` is not the primary meter of the facility - `INVALID_IMAGE_TYPE` — image extension not in the allowed list (png, gif, jpg, jpeg) - `INVALID_IMAGE` — file MIME type does not match the declared extension - `INVALID_DESCRIPTION` — description is missing, empty, or exceeds 512 characters content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 422 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ApiResponseDTO' type: object '500': description: >- Internal Server Error. Possible `error` codes: - `IMAGE_UPLOAD_FAILED` — the document storage service returned an error - `DOCUMENT_REFERENCE_MISSING` — the upload succeeded but returned no document reference - `UPDATE_FAILED` — the database update of the reading record failed content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ApiResponseDTO' type: object /facilities/{facility_id}/generation/meters/{meter_id}/image-readings/{processing_id}: get: tags: - Meter Readings summary: Get Meter Reading Image Processing Status description: >- Poll for OCR completion and report failures. Returns the status of a submitted meter reading image OCR processing. The `processing_id` is returned by the POST endpoint when uploading an image. operationId: getMeterReadingImageProcessingStatus parameters: - name: facility_id in: path description: The unique identifier of the facility required: true schema: type: integer example: 158830 minimum: 1 - name: meter_id in: path description: The unique identifier of the meter required: true schema: type: integer example: 140561 minimum: 1 - name: processing_id in: path description: The processing ID returned from the image upload (POST) request required: true schema: type: integer example: 12345 minimum: 1 responses: '200': description: OCR processing status and extracted data content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/MeterReadingImageProcessingStatus' type: object '400': description: Bad Request - Invalid facility_id or access denied content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: >- #/components/schemas/MeterReadingImageProcessingStatusBadRequestError type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '404': description: Not Found - Invalid processing id content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 404 elements: type: integer example: 0 type: object - properties: data: $ref: >- #/components/schemas/MeterReadingImageProcessingStatusNotFoundError type: object '500': description: Internal Server Error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: >- #/components/schemas/MeterReadingImageProcessingStatusServerError type: object /facilities/{facility_id}/generation/meters/{meter_id}/readings/{reading_id}: patch: tags: - Meter Readings summary: Update an Existing Meter Reading description: >- Partially update an existing meter reading for a facility. Updates are **always manual** — there is no automated/OCR path for this endpoint. The request body accepts the same fields as the manual submission: `meter_reading` and (for cumulative meters) `meter_reading_date_taken`. **Validation and business rules applied by the service layer**: - The user must have the `REPORT_PRODUCTION` ACL permission - `facility_id`, `meter_id`, and `reading_id` must be valid positive integers - The request body must be valid JSON - The reading must exist and belong to the specified meter (`READING_NOT_FOUND` / `READING_METER_MISMATCH`) - The reading must still be within the edit window (`OUTSIDE_EDIT_WINDOW`) - Field-level validation (e.g. required `meter_reading`, valid date format) is enforced and reported via the `errors` map in the response body operationId: updateMeterReading parameters: - name: facility_id in: path description: The unique identifier of the facility required: true schema: type: integer example: 12345 minimum: 1 - name: meter_id in: path description: The unique identifier of the meter required: true schema: type: integer example: 67890 minimum: 1 - name: reading_id in: path description: The unique identifier of the meter reading to update required: true schema: type: integer example: 54321 minimum: 1 requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ManualMeterReadingRequest' responses: '200': description: Meter reading updated successfully content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/MeterReadingSuccessResponse' type: object '400': description: >- Bad Request. Possible `error` codes: - `invalid_parameters` (from controller) — one or more path IDs are not valid positive integers - `invalid_json` (from controller) — request body is not valid JSON - `submission_failed` (from service) — field-level validation failed; see `errors` map - `OUTSIDE_EDIT_WINDOW` — the reading has already been processed by the registry and can no longer be edited - `READING_NOT_FOUND` — reading does not exist - `READING_METER_MISMATCH` — reading does not belong to the given meter - `VALIDATION_ERROR` — general validation failure with field-level details in `errors` content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/MeterReadingErrorResponse' type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '403': description: >- Forbidden. Possible `error` codes: - `permission_denied` — user lacks the `REPORT_PRODUCTION` ACL permission content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/MeterReadingErrorResponse' type: object '500': description: Internal Server Error — unexpected exception during update content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/MeterReadingErrorResponse' type: object /ct/company_entities: get: tags: - Clean Transportation summary: Company Entities description: Retrieve list of company entities. operationId: getCompanyEntities responses: '200': description: List of company entities content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/CompanyEntityList' type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '500': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ServerError' type: object /ct/assets: post: tags: - Clean Transportation summary: Create asset description: Create new Asset. operationId: createAsset requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/postAssetCreate' responses: '201': description: Asset created successfully content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 201 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/CreateAssetSuccess' type: object '400': description: Bad Request - Invalid input data content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/CreateAssetFieldErrors' type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object '500': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ServerError' type: object /market_pricing: get: tags: - Market summary: Market Pricing description: >- Get market pricing. The data available depends on the Insight subscription and is the same than the one available on Insight page on www.ms.xpansiv.com. operationId: searchMarket parameters: - name: state in: query description: | Search market pricing by state Example: PA required: false schema: type: string - name: product in: query description: | Search market pricing by product Example: PA2018 required: false style: form explode: false schema: type: array items: type: string - name: type in: query description: | Search market pricing by type bid or offer - bid - offer required: false schema: type: string - name: start_date in: query description: | Search market pricing after that date Example: 2017-10-01 required: false schema: type: string - name: end_date in: query description: | Search market pricing before that date Example: 2017-12-01 required: false schema: type: string - name: page in: query description: Indicate which page will be returned if the result is paginated required: false schema: type: integer responses: '200': description: >- List of market pricing available for the user and matching search criteria content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/MarketPricingListResponse' type: object '401': description: Unauthorized content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 401 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AuthError' type: object /qualified_reporting_entities: get: tags: - Qualified reporting entities summary: List qualified reporting entities description: All QRE ids ordered by id. operationId: getQualifiedReportingEntities responses: '200': description: Success — standard API envelope; QRE rows in `data`. content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: >- #/components/schemas/QualifiedReportingEntitiesListResponse type: object '400': description: Bad request - authorization or validation errors content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ValidationError' type: object '403': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/Error' type: object '500': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ServerError' type: object security: - BearerAuth: [] /building_types: get: tags: - Building types summary: List building types description: All building types ordered by name operationId: getBuildingTypes responses: '200': description: Success — standard API envelope; building types in `data`. content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/BuildingTypesListResponse' type: object '400': description: Bad request - authorization or validation errors content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ValidationError' type: object '403': description: Access forbidden content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AccessForbiddenError' type: object '500': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ServerError' type: object security: - BearerAuth: [] /remote_data_collectors: get: tags: - Remote data collectors summary: List remote data collectors description: All remote data collectors ordered by name. operationId: getRemoteDataCollectors responses: '200': description: Success — standard API envelope; remote data collectors in `data`. content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/RemoteDataCollectorsListResponse' type: object '400': description: Bad request - authorization or validation errors content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ValidationError' type: object '403': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/AccessForbiddenError' type: object '500': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ServerError' type: object security: - BearerAuth: [] /utilities: get: tags: - Utilities summary: List utilities by county and state description: Returns utilities matching optional `county` and `state` parameters. operationId: getUtilitiesByCountyAndState parameters: - name: county in: query description: County name filter (passed through to the data layer). required: false schema: type: string - name: state in: query description: State filter; compared using an uppercase form of this value. required: false schema: type: string responses: '200': description: Success — standard API envelope; utilities in `data`. content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 200 type: object - properties: data: $ref: '#/components/schemas/UtilitiesListResponse' type: object '400': description: Bad request - authorization or validation errors content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 400 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ValidationError' type: object '403': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 403 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/Error' type: object '500': description: Internal error content: application/json: schema: type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - properties: code: type: integer example: 500 elements: type: integer example: 0 type: object - properties: data: $ref: '#/components/schemas/ServerError' type: object security: - BearerAuth: [] components: schemas: UpdateMemberPermissions: description: Replace the role assignments for an account member. required: - role_ids properties: role_ids: description: >- Role ids to assign. Must be a subset of {1, 2, 3} and must include role 1 (basic/view). type: array items: type: integer example: - 1 - 2 type: object AccountMember: description: An account member and their assigned roles. required: - membership_id - user_id - email - first_name - last_name - active - role_ids properties: membership_id: type: integer example: 555 user_id: type: integer example: 42 email: type: string format: email example: member@example.com first_name: type: string example: Jane last_name: type: string example: Doe active: type: boolean example: true role_ids: type: array items: type: integer example: - 1 - 2 type: object AccountMemberList: description: Members of an account and their role assignments. required: - members properties: members: type: array items: $ref: '#/components/schemas/AccountMember' type: object RemoveMemberResult: description: Result of removing a member from an account. required: - membership_id - removed - caller_active_account_id properties: membership_id: type: integer example: 555 removed: type: boolean example: true caller_active_account_id: description: >- The authenticated caller's effective active account after removal (re-resolved if the caller removed themselves). type: integer example: 1001 type: object UpdateAccountNameRequest: description: Partial account update payload. Phase 1 supports account_name only. required: - account_name properties: account_name: description: Trimmed display name for the account. type: string example: Acme Solar LLC maxLength: 50 type: object AccountNameUpdateResponse: description: Updated account name fields returned in the API envelope `data` field. required: - account_id - account_name - account_modified properties: account_id: type: integer example: 4242 account_name: type: string example: Acme Solar LLC account_modified: description: Last modification timestamp in ISO-8601 (Atom). type: string format: date-time example: '2024-06-01T14:30:00-04:00' type: object ApiKeyCreateRequestBody: required: - account_id properties: account_id: type: integer format: int64 type: object ApiKeyCreatePayload: required: - api_access_id - account_id - token - active - created_date properties: api_access_id: type: integer format: int64 account_id: type: integer format: int64 token: type: string active: type: boolean created_date: type: string type: object ApiKeyDeactivatePayload: required: - api_access_id - account_id - active - edited_date properties: api_access_id: type: integer format: int64 account_id: type: integer format: int64 active: type: boolean example: false edited_date: type: string nullable: true type: object ApiKeyList: description: API key rows in the API envelope `data` field. type: array items: $ref: '#/components/schemas/ApiKeyListRow' ApiKeyListRow: required: - api_access_id - account_id - account_name - token - active - created_date properties: api_access_id: type: integer format: int64 account_id: type: integer format: int64 account_name: type: string token: description: Always null on GET list. type: string nullable: true active: type: boolean created_date: type: string edited_date: type: string nullable: true type: object ApiKeyRotatePayload: required: - api_access_id - account_id - token - active - edited_date properties: api_access_id: type: integer format: int64 account_id: type: integer format: int64 token: type: string active: type: boolean edited_date: type: string nullable: true type: object AuthTokenError: description: >- Error from auth token exchange or refresh (flat JSON, not the standard API envelope). required: - error properties: error: type: string example: Invalid or expired auth code type: object TokenExchangeRequest: description: >- Primary PWA clients send `application/x-www-form-urlencoded`. Mobile clients (`X-Client-Type: mobile`) may send the same field as JSON. required: - code properties: code: description: One-time key received from login redirect (32 characters). type: string example: thirty-two-characters-in-len-str maxLength: 32 minLength: 32 type: object TokenRefreshRequest: description: >- Optional body for mobile clients. PWA clients omit the body and send the refresh_token via an HTTP-only cookie. properties: refresh_token: description: Refresh token (mobile clients only). type: string maxLength: 32 minLength: 32 type: object AuthTokenResponse: description: >- JWT issued by auth token exchange or refresh. `refresh_token` is included for mobile clients only. required: - jwt properties: jwt: description: JWT token for authenticated API access type: string example: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... refresh_token: description: Rotated refresh token (mobile clients only). type: string nullable: true maxLength: 32 minLength: 32 type: object BuildingTypeItem: description: >- One building type row in the API envelope `data` array for `getBuildingTypes`. Runtime: {@see \Building_type_lib::get_building_types()} — `id`, `name`. required: - id - name properties: id: description: Primary key (`building_type_id`). type: integer name: description: Display name (`building_type_name`). type: string type: object BuildingTypesListResponse: description: Building type rows returned in the API envelope `data` field. type: array items: $ref: '#/components/schemas/BuildingTypeItem' CountriesListResponse: description: Country rows returned in the API envelope `data` field. type: array items: $ref: '#/components/schemas/Country' Country: description: One country row in the API envelope `data` array for `getCountries`. required: - country_id - country_name properties: country_id: description: Primary key (`country_id`). type: integer country_name: description: Display name (`country_name`). type: string type: object StateOption: description: One state or province option in the `getStatesByCountry` response. required: - code - name properties: code: description: Two-letter state or province code. type: string name: description: Display name. type: string type: object StatesByCountryResponse: description: Map of country_id (string key) to state/province options. type: object additionalProperties: type: array items: $ref: '#/components/schemas/StateOption' CreateAssetFieldErrors: description: >- Field-level validation messages in envelope `data` for `POST /ct/assets`. properties: asset_fse_id: type: string example: The asset_fse_id FSE12345EE6 already exists. reporting_method_id: type: string example: The reporting_method_id field is required. address_latitude: type: string example: >- The address_latitude value is not valid within United States limits. It should be within the range of 7.2 and 83.7. asset_serial_number: type: string example: The asset_serial_number field is required. entity_id: type: string asset_name: type: string address_street: type: string address_city: type: string address_zip: type: string address_state: type: string address_country: type: string address_longitude: type: string class_type_id: type: string charging_type_id: type: string manufacturer_name: type: string asset_registration_upload_id: type: string asset_start_date: type: string asset_end_date: type: string electricity_source_id: type: string type: object postAssetCreate: description: Request body for `POST /ct/assets` (`createAsset`). required: - entity_id - asset_name - address_street - address_city - address_zip - address_state - address_country - address_latitude - address_longitude - class_type_id - asset_serial_number - charging_type_id - manufacturer_name - asset_fse_id - asset_registration_upload_id - reporting_method_id - asset_start_date - asset_end_date - electricity_source_id properties: entity_id: description: Unique identifier for the entity type: string example: '150' asset_name: description: Name of the asset; can include dynamic placeholders type: string example: Test asset address_street: description: Street address of the asset type: string example: 40270 GLENALDER PLACE address_city: description: City where the asset is located type: string example: Squamish address_zip: description: ZIP or postal code type: string example: '12345' address_state: description: State or province type: string example: CA address_country: description: Country code (ISO format) type: string example: US address_latitude: description: Latitude coordinate; can include dynamic placeholders type: string example: '55.391234' address_longitude: description: Longitude coordinate; can include dynamic placeholders type: string example: '-92.75123' class_type_id: description: List of class type IDs associated with the asset type: array items: type: integer example: - 3 asset_serial_number: description: Serial number for the asset; can include dynamic placeholders type: string example: '2025410189123' charging_type_id: description: Identifier for the charging type type: integer example: 3 manufacturer_name: description: Name of the manufacturer type: string example: ABCD company asset_fse_id: description: FSE ID type: string example: FSE413000EE2 asset_registration_upload_id: description: Unique registration upload ID; can include placeholders type: string example: RUZ-781234 reporting_method_id: description: ID of the reporting method type: string example: '2' asset_start_date: description: Asset start date in YYYY-MM-DD format type: string format: date example: '2021-07-01' asset_end_date: description: Asset end date in YYYY-MM-DD format type: string format: date example: '2026-11-02' electricity_source_id: description: ID representing the source of electricity type: integer example: 3 asset_status_id: description: Status ID of the asset type: string example: '4' asset_fse_code: description: Code representing the FSE type: string example: Test FSE code asset_method_of_kwh: description: Method used to measure kWh type: string example: akhdkajh asset_charging_data_source: description: Source of charging data type: string example: Test asset_lcfs_lff_id: description: LCFS (Low Carbon Fuel Standard) LFF ID type: string example: '1' asset_lcfs_facility_name: description: Name of the LCFS facility type: string example: test asset_rp_name: description: Reporting party or responsible person/entity name type: string example: test type: object CompanyEntityAddress: description: Address nested on a company entity returned by `getCompanyEntities`. properties: address_id: type: integer example: 18353 address_street: type: string example: 1234 Shattuck Avenue address_city: type: string example: Berkeley address_zip: type: string example: '94704' address_state: type: string example: CA address_country: type: string example: US type: object CompanyEntityContact: description: Contact nested on a company entity returned by `getCompanyEntities`. properties: contact_id: type: string example: '12345' contact_name: type: string example: John Doe contact_email: type: string example: test.address@example.com contact_phone: type: string example: '123456789' contact_date_created: type: string example: '2024-10-26 13:30:24' type: object CompanyEntityItem: properties: address: $ref: '#/components/schemas/CompanyEntityAddress' contact: $ref: '#/components/schemas/CompanyEntityContact' entity_id: type: string example: '172' entity_name: type: string example: Example entity name entity_fein: type: string example: '123456789' entity_agreement: type: string example: 6363fac17cfaf0_1234567.pdf type: object CompanyEntityList: description: Company entity rows in the API envelope `data` field. type: array items: $ref: '#/components/schemas/CompanyEntityItem' CreateAssetSuccess: description: >- Success payload in the API envelope `data` field for `createAsset` (`POST /ct/assets`). required: - status properties: status: type: string example: asset created type: object FacilityCompleteError: description: >- Error payload for `POST /facilities/{facility_id}/complete` business-rule and validation failures. required: - error - message properties: error: type: string example: error message: type: string example: Facility data validation failed. details: description: >- Optional structured context. Shape depends on the error: field validation map (`{ "field": "error message" }`), list of state codes (`["NJ"]`), or missing document slugs keyed by state (`{ "PA": ["schedule_a"] }`). Document slugs match `upload_field_name` from `GET /facilities/{facility_id}/state_eligibilities`. nullable: true oneOf: - type: object additionalProperties: type: string - type: array items: type: string - type: object additionalProperties: type: array items: type: string type: object FacilityGetError: required: - error - message type: object allOf: - $ref: '#/components/schemas/Error' - properties: error: description: >- GET `/facilities/{facility_id}` when the facility is missing or not visible to the account. type: string example: error message: type: string example: Wrong facility_id parameter type: object FacilityGetMissingIdError: required: - error - message type: object allOf: - $ref: '#/components/schemas/Error' - properties: error: description: >- GET `/facilities/{facility_id}` when the path parameter is missing. type: string example: error message: type: string example: Missing facility_id parameter type: object MeterReadingEligibilityBadRequestError: description: >- `GET .../readings/eligibility` when facility or meter path IDs are invalid (manual contract). required: - error - message type: object allOf: - $ref: '#/components/schemas/Error' MeterReadingEligibilityServerError: description: >- `GET .../readings/eligibility` when an unexpected error occurs (manual contract). required: - can_submit - code - message properties: can_submit: type: boolean example: false code: type: string example: UNKNOWN message: type: string example: >- Error occurred while checking meter reading eligibility. Please try again later. type: object MeterReadingImageProcessingStatusBadRequestError: description: >- `GET .../image-readings/{processing_id}` when facility access or path validation fails (manual documents as 400). required: - success - error - message type: object allOf: - $ref: '#/components/schemas/MeterReadingOperationError' MeterReadingImageProcessingStatusNotFoundError: description: >- `GET .../image-readings/{processing_id}` when the processing record is missing or not owned by the facility. required: - success - error - message type: object allOf: - $ref: '#/components/schemas/MeterReadingOperationError' - properties: error: type: string example: invalid_processing_id enum: - invalid_processing_id type: object MeterReadingImageProcessingStatusServerError: description: >- `GET .../image-readings/{processing_id}` internal server error (manual contract). required: - success - error - message type: object allOf: - $ref: '#/components/schemas/MeterReadingOperationError' ProcessMeterReadingImageBadRequestError: description: >- `POST .../image-readings` client errors (missing/invalid image per manual spec). required: - success - error - message type: object allOf: - $ref: '#/components/schemas/MeterReadingOperationError' - properties: error: description: Error code indicating the type of validation failure type: string example: no_image enum: - no_image - invalid_image message: description: Human-readable error message type: string example: No image uploaded type: object ProcessMeterReadingImageMaxAttemptsError: description: >- `POST .../image-readings` when the photo OCR retry limit is reached (HTTP 429). required: - success - error - message - requires_manual_entry - remaining_attempts properties: success: type: boolean example: false error: description: Error code indicating the retry limit has been reached type: string example: max_photo_attempts_reached enum: - max_photo_attempts_reached message: description: Human-readable error message type: string example: Maximum photo submission attempts reached. Please use manual entry. requires_manual_entry: description: Always true when retry limit is reached type: boolean example: true remaining_attempts: description: Always 0 when retry limit is reached type: integer example: 0 minimum: 0 type: object ProcessMeterReadingImageServerError: description: '`POST .../image-readings` server failures after the image is accepted.' required: - success - error - message type: object allOf: - $ref: '#/components/schemas/MeterReadingOperationError' - properties: error: description: Error code indicating the type of server failure type: string example: image_upload_failed enum: - image_upload_failed - document_reference_missing - unexpected_error message: description: Human-readable error message type: string example: Image upload failed type: object ProductionHistoryBadRequestError: description: >- `GET .../generation/history` when generation data cannot be loaded (runtime returns 400). required: - error - message type: object allOf: - $ref: '#/components/schemas/Error' MeterReadingErrorResponse: description: >- Error response payload (inside the data envelope). The `error` and `errors` fields are optional and may not be present in all error responses. required: - success - message properties: success: description: Indicates if the submission was successful (always false for errors) type: boolean example: false error: description: Error code identifying the specific error type (optional) type: string example: VALIDATION_ERROR enum: - MULTIPLE_FACILITIES - NO_FACILITY - FIRST_TIME_SUBMISSION - CUMULATIVE_ONLY - AUTO_REPORTING - METER_NOT_VERIFIED - NEPOOL_REGISTRY - WREGIS_REGISTRY - NOT_ELIGIBLE - NO_METER - UNAUTHORIZED - INACTIVE_FACILITY - METER_ID_MISMATCH - VALIDATION_ERROR - OUTSIDE_EDIT_WINDOW - READING_NOT_FOUND - READING_METER_MISMATCH message: description: Human-readable error message (always present) type: string example: Reading falls outside of the accepted date range. errors: description: Field-specific validation errors (optional) type: object example: meter_reading: Reading is required meter_reading_date_taken: Reading date is required for cumulative meters additionalProperties: type: string type: object TransactionsForbiddenError: description: >- `GET .../transactions` when access is denied or the facility is closed/lost (manual 403). required: - error - message type: object allOf: - $ref: '#/components/schemas/Error' TransactionsInvalidFacilityIdError: description: >- `GET .../transactions` when `facility_id` is not numeric (manual contract). required: - error - message type: object allOf: - $ref: '#/components/schemas/Error' TransactionsNotFoundError: required: - error - message type: object allOf: - $ref: '#/components/schemas/Error' - properties: error: description: >- `GET .../transactions` when the facility is missing or transaction data cannot be loaded. type: string example: error enum: - error - facility_not_found message: type: string example: Facility not found type: object AssignExistingPaymentPreferenceRequest: required: - existing_payment_method properties: existing_payment_method: $ref: '#/components/schemas/ExistingPaymentMethodSelection' abp_terms_agreement_id: type: integer nullable: true minimum: 1 type: object CreateBankPaymentPreferenceRequest: required: - bank_name - account_holder - account_type - account_number - account_routing properties: bank_name: type: string maxLength: 60 account_holder: type: string maxLength: 60 account_type: type: integer account_number: type: string maxLength: 17 account_routing: type: string maxLength: 9 minLength: 9 bank_account_holder_type: type: string nullable: true enum: - Personal - Business institution_number: type: string nullable: true transit_number: type: string nullable: true abp_terms_agreement_id: type: integer nullable: true minimum: 1 type: object CreateCheckPaymentPreferenceRequest: required: - payable_to - street - city - state - zip properties: payable_to: type: string maxLength: 80 street: type: string maxLength: 80 apartment_suite: type: string nullable: true maxLength: 80 city: type: string maxLength: 80 state: type: string zip: type: string example: '02110' type: object ExistingPaymentMethodSelection: required: - id - financial_type properties: id: type: string example: '12' financialType: type: string example: bank enum: - bank - check type: object AbpTermsStatus: properties: active_terms_id: type: integer nullable: true agreed_terms_id: type: integer nullable: true agreed_at: type: string nullable: true needs_agreement: type: boolean type: object AccountTypeOption: properties: id: type: integer label: type: string type: object BankPaymentPreferenceDetails: properties: bank_name: type: string nullable: true account_holder_name: type: string nullable: true account_holder_type: type: string nullable: true account_type: type: string nullable: true routing_number_display: type: string nullable: true account_number_display: type: string nullable: true transit_number_display: type: string nullable: true institution_number_display: type: string nullable: true can_show_sensitive_display_fields: type: boolean type: object CheckPaymentPreferenceDetails: properties: payable_to: type: string nullable: true street: type: string nullable: true apartment_suite: type: string nullable: true city: type: string nullable: true state: type: string nullable: true zip: type: string nullable: true type: object ExistingPaymentMethodOption: properties: id: type: string example: '12' financial_type: type: string enum: - bank - check label: type: string type: object FacilityPaymentPreferenceResponse: properties: facility_id: type: integer facilities_groups_id: type: integer account_id: type: integer facility_name: type: string currency_id: type: integer is_abp: type: boolean can_edit: type: boolean edit_blocked_reason: type: string nullable: true enum: - forbidden - maintenance - locked preference: oneOf: - $ref: '#/components/schemas/PaymentPreferenceDetails' nullable: true edit_options: oneOf: - $ref: '#/components/schemas/PaymentPreferenceEditOptions' nullable: true type: object PaymentPreferenceDetails: properties: preference_id: type: integer type: type: string enum: - bank - check type_label: type: string is_locked: type: boolean is_active: type: boolean created_at: type: string nullable: true bank: oneOf: - $ref: '#/components/schemas/BankPaymentPreferenceDetails' nullable: true check: oneOf: - $ref: '#/components/schemas/CheckPaymentPreferenceDetails' nullable: true abp_terms: oneOf: - $ref: '#/components/schemas/AbpTermsStatus' nullable: true type: object PaymentPreferenceEditOptions: properties: allow_existing_payment_method_selection: type: boolean allow_new_bank_account: type: boolean allow_new_check: type: boolean existing_payment_methods: type: array items: $ref: '#/components/schemas/ExistingPaymentMethodOption' account_type_options: type: array items: $ref: '#/components/schemas/AccountTypeOption' required_abp_terms_id: type: integer nullable: true type: object AddFacilityStateCertificationRequest: description: Request body for `POST /facilities/{facility_id}/state_certifications`. required: - certificates properties: certificates: type: array items: $ref: '#/components/schemas/StateCertificationInput' minItems: 1 type: object AutomatedMeterReadingRequest: description: >- Automated meter reading submission body for `submitMeterReading` (OCR processing_id). required: - processing_id properties: processing_id: description: >- ID from the image-readings OCR process containing automated meter reading data type: integer example: 42 minimum: 1 type: object BaseMeterReading: description: >- Internal meter-reading command context (populated server-side; not part of the public JSON body). type: object CompleteFacilityRequest: description: Request body for `POST /facilities/{facility_id}/complete`. required: - agree_to_terms_and_conditions properties: agree_to_terms_and_conditions: description: >- Must be `true` to confirm agreement to Xpansiv Managed Solutions terms and conditions. Runtime also accepts `1`, `"true"`, `"yes"`, and `"Yes"`. type: boolean example: true states: description: >- State abbreviations to complete. When omitted or empty, all eligible zero-fee states are completed. type: array items: type: string maxLength: 2 example: - PA - MD nullable: true type: object CreateFacilityStateApplicationRequest: description: Request body for `POST /facilities/{facility_id}/state_applications`. required: - state properties: state: type: string example: PA maxLength: 2 type: object facilityAbpCreateOrUpdate: description: ABP facility create payload in nested DTO format. allOf: - $ref: '#/components/schemas/facilityCreateOrUpdateCommonProperties' - $ref: '#/components/schemas/FacilityAbpFields' FacilityAbpFields: description: >- Illinois Adjustable Block Program (ABP) fields. Required when creating an ABP facility (`address.state` is `IL` and your account is ABP-enabled). See nested `panels[].bifacial` and `inverters[].efficiency` on the parent schema. properties: installer_demographics: oneOf: - $ref: '#/components/schemas/InstallerDemographics' nullable: true description: >- Installer workforce demographics required for Illinois ABP applications. existing_project_application_id: description: >- ABP application ID when this facility is an expansion of an existing project on the same parcel. type: string example: ABP-12345 maxLength: 20 pv_system_disclosure_form_id: description: Illinois PV System Disclosure Form identifier. type: string example: '12345' custom_capacity_factor: description: >- Custom capacity factor as a decimal (e.g. `0.1426` for 14.26%). Required when `rec_estimate_methodology` is `CUSTOM_CAPACITY_FACTOR`. Do not send a percentage scaled to 100. type: number format: float example: 0.1426 custom_capacity_explanation: description: >- Narrative explaining the custom capacity factor when `rec_estimate_methodology` is `CUSTOM_CAPACITY_FACTOR`. type: string icc_docket_number: description: Illinois Commerce Commission docket number when applicable. type: string facility_energized: description: >- Whether the facility has been energized / received permission to operate. type: boolean example: true net_metering_approved: description: >- Whether net metering has been approved. Drives which net-metering-related documents appear in `GET /facilities/{id}/state_eligibilities`. type: boolean example: true install_contract_exec_date: description: Installation contract execution date (`YYYY-MM-DD`). type: string format: date example: '2025-06-01' financial_structure: description: >- How the system is financed. PPA is common for leased portfolio operators. type: string example: PPA enum: - Customer-owned - Lease - PPA is_already_completed_project_app_on_the_same_parcel_of_land: description: >- Whether a project application was already completed on the same parcel of land. type: boolean example: false is_project_located_on_public_school_owned_land: description: Whether the project is located on public school-owned land. type: boolean example: false rec_estimate_methodology: description: >- Method used to estimate REC production. Use `CUSTOM_CAPACITY_FACTOR` when supplying `custom_capacity_factor`. type: string example: CUSTOM_CAPACITY_FACTOR enum: - PVWATTS - CUSTOM_CAPACITY_FACTOR ground_cover_ratio: description: Ground cover ratio for the installation when applicable. type: number format: float prevailing_wage_subject: description: Whether the project is subject to prevailing wage requirements. type: boolean minimal_shading_criteria: description: Whether the site meets minimal shading criteria. type: boolean qualified_installer_person: description: Name of the qualified installer contact for the ABP application. type: string construction_activities_completion_date: description: Date construction activities were completed (`YYYY-MM-DD`). type: string format: date example: '2015-05-15' is_expansion: description: >- Whether this application is an expansion of an existing system. Defaults to `false` when omitted. type: boolean example: false type: object facilityAbpFlatCreateOrUpdate: description: >- Illinois ABP facility create payload in legacy flat format. Requires `state`: IL. Uses `postAbpPanelCreate` for `panels` and `postAbpInverterCreate` for `inverters`. Your account must be ABP-enabled. allOf: - $ref: '#/components/schemas/facilityFlatCreateOrUpdateCommonProperties' - $ref: '#/components/schemas/FacilityCreateAbpFields' FacilityCreateAbpFields: required: - state - name - i_am_owner - i_have_solar_production_meter - facility_type - facility_size - is_battery_backup - street - city - county - zip - installer_company - installer_street - installer_city - installer_state - installer_zip - referrer_company - owner_first_name - owner_last_name - owner_email - owner_street - owner_city - owner_state - owner_zip - owner_phone - owner_county - system_cost - meter_manufacturer - meter_model_type - meter_remote_data_collector - online_monitoring_api_id - utility_interconnection_date - utility_name - facility_energized - net_metering_approved - abp_block_app_pv_system_disclosure_form_id - abp_num_recs_capacity_factor - rec_estimate_methodology - ground_cover_ratio - prevailing_wage_subject - minimal_shading_criteria - icc_docket_number - install_contract_exec_date - financial_structure - is_already_completed_project_app_on_the_same_parcel_of_land - is_project_located_on_public_school_owned_land - is_expansion - abp_installer_demographics_hours_race_white - abp_installer_demographics_hours_race_black_or_aa - abp_installer_demographics_hours_race_ai_or_an - abp_installer_demographics_hours_race_asian - abp_installer_demographics_hours_race_h_or_pi - abp_installer_demographics_hours_race_multi - abp_installer_demographics_hours_race_other - abp_installer_demographics_hours_race_decline - abp_installer_demographics_hours_eth_hisp_lat - abp_installer_demographics_hours_eth_not_hisp_lat - abp_installer_demographics_hours_eth_decline - abp_installer_demographics_train_solar - abp_installer_demographics_train_craft - abp_installer_demographics_train_multi_cult properties: state: description: ABP rules require Illinois. type: string example: IL enum: - IL installer_company: type: string example: Installer company name maxLength: 100 installer_street: type: string example: 7312 Random Street maxLength: 80 installer_city: type: string example: Chicago maxLength: 80 installer_state: type: string example: IL installer_zip: type: string example: '60601' owner_phone: type: string example: 122-223-3321 owner_county: type: string example: York County maxLength: 80 facility_energized: description: >- Whether the facility has been energized / received permission to operate. example: 1 oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' - type: boolean net_metering_approved: description: Whether net metering has been approved. example: 1 oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' - type: boolean abp_block_app_pv_system_disclosure_form_id: description: Illinois PV System Disclosure Form identifier. example: '12345' oneOf: - type: string - type: integer abp_num_recs_capacity_factor: description: >- Custom capacity factor as a decimal (e.g. `0.1426` for 14.26%). Required when `rec_estimate_methodology` is `CUSTOM_CAPACITY_FACTOR`. type: number format: float example: 0.1426 rec_estimate_methodology: type: string example: CUSTOM_CAPACITY_FACTOR enum: - PVWATTS - CUSTOM_CAPACITY_FACTOR ground_cover_ratio: type: number format: float prevailing_wage_subject: oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' - type: boolean minimal_shading_criteria: oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' - type: boolean icc_docket_number: type: string install_contract_exec_date: type: string example: '2025-06-01' financial_structure: type: string example: PPA enum: - Customer-owned - Lease - PPA is_already_completed_project_app_on_the_same_parcel_of_land: oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' - type: boolean is_project_located_on_public_school_owned_land: oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' - type: boolean is_expansion: example: 0 oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' - type: boolean abp_installer_demographics_hours_race_white: type: number example: 100 abp_installer_demographics_hours_race_black_or_aa: type: number example: 50 abp_installer_demographics_hours_race_ai_or_an: type: number example: 0 abp_installer_demographics_hours_race_asian: type: number example: 25 abp_installer_demographics_hours_race_h_or_pi: type: number example: 0 abp_installer_demographics_hours_race_multi: type: number example: 10 abp_installer_demographics_hours_race_other: type: number example: 5 abp_installer_demographics_hours_race_decline: type: number example: 0 abp_installer_demographics_hours_eth_hisp_lat: type: number example: 30 abp_installer_demographics_hours_eth_not_hisp_lat: type: number example: 160 abp_installer_demographics_hours_eth_decline: type: number example: 0 abp_installer_demographics_train_solar: type: number example: 40 abp_installer_demographics_train_craft: type: number example: 20 abp_installer_demographics_train_multi_cult: type: number example: 10 installer_first_name: type: string example: John maxLength: 80 installer_last_name: type: string example: Doe maxLength: 80 installer_email: type: string example: contact@email.com maxLength: 100 installer_phone: type: string example: 123-123-1234 installer_apartment_suite: type: string maxLength: 80 installer_county: type: string example: Cook County maxLength: 80 abp_existing_project_application_id: description: Required when `is_expansion` is `1`. type: string example: ABP-12345 maxLength: 20 abp_block_app_custom_capacity_explanation: description: >- Required when `rec_estimate_methodology` is `CUSTOM_CAPACITY_FACTOR`. type: string qualified_installer_person: description: Required when `utility_interconnection_date` is set. type: string construction_activities_completion_date: description: Required when `utility_interconnection_date` is set. type: string example: '2015-05-15' registry_id_number: type: string example: NON143221 maxLength: 10 minLength: 4 external_id: type: string example: EXTERNAL-123 maxLength: 100 panels: type: array items: $ref: '#/components/schemas/postAbpPanelCreate' inverters: type: array items: $ref: '#/components/schemas/postAbpInverterCreate' type: object FacilityCreateDggFields: required: - state - name - i_am_owner - i_have_solar_production_meter - facility_type - facility_size - is_battery_backup - street - city - county - zip - referrer_company - owner_first_name - owner_last_name - owner_email - owner_street - owner_city - owner_state - owner_zip - meter_accuracy - utility_interconnection_date - meter_serial_number - meter_manufacturer - meter_model_type - can_provide_solar_production_data_access - utility_name - utility_account_number properties: state: description: DGG rules require California. type: string example: CA enum: - CA can_provide_solar_production_data_access: description: >- `true` / `1` — send `meter_remote_data_collector` and `online_monitoring_api_id`; omit QRE fields. `false` / `0` — send `qualified_reporting_entity` and `qualified_reporting_entity_id_value`; omit remote monitoring fields. oneOf: - type: boolean - type: integer enum: - 0 - 1 utility_account_number: description: >- Optional for DGG when a Utility Bill is uploaded separately via `POST /facilities/{id}/documents`. nullable: true oneOf: - type: string maxLength: 50 - type: integer dgg: description: >- Set `true` so the request is treated as DGG when your account is DGG-enabled. type: boolean example: true group_name: description: >- Optional distributed-generation group display name (maps to registry facility group name). type: string maxLength: 255 leasing_company: description: Required when `system_leased` is `1`. type: string maxLength: 80 qualified_reporting_entity: description: >- QRE name matching `GET /qualified_reporting_entities` when not using production data access. type: string maxLength: 255 qualified_reporting_entity_id_value: description: QRE / utility identifier when not using production data access. nullable: true oneOf: - type: string maxLength: 255 - type: integer max_annual_energy: type: number panels: type: array items: $ref: '#/components/schemas/postDggPanelCreate' type: object facilityCreateOrUpdateCommonProperties: description: >- Payload format accepted by `POST /facilities` and `PATCH /facilities/{facility_id}`. Legacy flat payload keys are still accepted for backwards compatibility and will be deprecated in a future release. required: - name - i_am_owner - has_solar_production_meter - facility_type - facility_size - address - installer - referrer_company - owner - system_cost - meter properties: name: type: string example: Facility name maxLength: 80 i_am_owner: description: >- Whether the submitter is the generation attribute owner (`0` / `1`; string forms are coerced). example: 1 nullable: true oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' - type: boolean has_solar_production_meter: example: 1 nullable: true oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' - type: boolean facility_type: description: |- Possible values are: - RES (Residential) - COM (Commercial) - MUNI (Municipal) - CMTY (Community Solar) type: string example: RES enum: - RES - COM - MUNI - CMTY facility_size: description: Facility DC capacity (DC kW). Minimum 0.000001. type: number example: 5.657 minimum: 0.000001 is_battery_backup: example: 0 oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' - type: boolean default: 0 system_leased: example: 0 oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' - type: boolean default: 0 leasing_company: description: Required when `system_leased` is `true`. type: string example: Leasing company name maxLength: 100 usable_energy: description: >- Required when `is_battery_backup` is `true`. Numeric strings are accepted. example: 2.36 nullable: true oneOf: - type: number minimum: 0.000001 - type: string system_cost: description: Numeric strings are accepted. example: 12345 nullable: true oneOf: - type: integer minimum: 1 - type: string referrer_company: type: string example: Referrer company name maxLength: 100 building_type: type: string example: Single Family Detached hostname: type: string example: Host name maxLength: 80 facility_location_type: type: string example: Rooftop enum: - Rooftop - Parking canopy - Brownfield - Other aggregate_net_metering: type: boolean example: true colocated_facility: type: boolean example: false cmty_application_number: type: string maxLength: 100 external_id: type: string example: EXTERNAL-123 maxLength: 100 comment_box: type: string dgg: type: boolean example: false address: $ref: '#/components/schemas/facilityAddressInput' owner: $ref: '#/components/schemas/facilityOwnerInput' installer: $ref: '#/components/schemas/facilityInstallerInput' meter: $ref: '#/components/schemas/facilityMeterInput' registry: $ref: '#/components/schemas/facilityRegistryInput' panels: type: array items: $ref: '#/components/schemas/facilityPanelInput' inverters: type: array items: $ref: '#/components/schemas/facilityInverterInput' type: object facilityCreateOrUpdate: description: Standard facility create payload in nested format. allOf: - $ref: '#/components/schemas/facilityCreateOrUpdateCommonProperties' FacilityCreateStandardFields: required: - name - i_am_owner - i_have_solar_production_meter - facility_type - facility_size - street - city - county - zip - state - installer_company - referrer_company - owner_first_name - owner_last_name - owner_street - owner_city - owner_state - owner_zip - owner_phone - owner_county - system_cost - meter_manufacturer - meter_model_type - utility_interconnection_date - utility_name - utility_account_number properties: state: type: string example: VA installer_company: type: string example: Installer company name maxLength: 100 owner_phone: type: string example: 122-223-3321 owner_county: type: string example: York County maxLength: 80 utility_account_number: type: string maxLength: 50 installer_first_name: type: string example: John maxLength: 80 installer_last_name: type: string example: Doe maxLength: 80 installer_email: type: string example: contact@email.com maxLength: 100 installer_phone: type: string example: 123-123-1234 installer_street: type: string example: 7312 Random Street maxLength: 80 installer_apartment_suite: type: string maxLength: 80 installer_city: type: string example: Yorktown maxLength: 80 installer_county: type: string example: York County maxLength: 80 installer_state: type: string example: VA installer_zip: type: string example: '23693' hostname: type: string example: Host name maxLength: 80 registry_id_number: type: string example: NON12345 maxLength: 10 minLength: 4 facility_location_type: type: string example: Rooftop enum: - Rooftop - Parking canopy - Brownfield - Other aggregate_net_metering: type: boolean example: true colocated_facility: type: boolean example: true cmty_application_number: type: string maxLength: 100 meter_last_date_certification: type: string example: '2016-08-02' current_production_reading: type: number example: 39.95 minimum: 0 date_of_production_reading: type: string example: '2018-10-17' panels: type: array items: $ref: '#/components/schemas/panelFlatCreate' type: object facilityDggCreateOrUpdate: description: California DGG facility create payload in nested format. allOf: - $ref: '#/components/schemas/facilityCreateOrUpdateCommonProperties' - $ref: '#/components/schemas/FacilityDggFields' FacilityDggFields: required: - can_provide_solar_production_data_access properties: can_provide_solar_production_data_access: description: >- `true` / `1` — send `meter.remote_data_collector` and `meter.online_monitoring_api_id`; omit QRE fields. `false` / `0` — send `qualified_reporting_entity` and `qualified_reporting_entity_id_value`; omit remote monitoring fields. oneOf: - type: boolean - type: integer enum: - 0 - 1 dgg: description: >- Set `true` so the request is treated as DGG when your account is DGG-enabled. type: boolean example: true group_name: description: >- Optional distributed-generation group display name (maps to registry facility group name). type: string maxLength: 255 qualified_reporting_entity: description: >- QRE name matching `GET /qualified_reporting_entities` when not using production data access. type: string maxLength: 255 qualified_reporting_entity_id_value: description: QRE / utility identifier when not using production data access. nullable: true oneOf: - type: string maxLength: 255 - type: integer max_annual_energy: type: number type: object facilityDggFlatCreateOrUpdate: description: >- California DGG facility create payload. Requires `dgg`: true and `state`: CA. Uses `postDggPanelCreate` for `panels` (not `panelFlatCreate`). allOf: - $ref: '#/components/schemas/facilityFlatCreateOrUpdateCommonProperties' - $ref: '#/components/schemas/FacilityCreateDggFields' facilityFlatCreateOrUpdateCommonProperties: description: >- Shared JSON properties for standard facility create (`facilityFlatCreateOrUpdate`), California DGG create (`facilityDggFlatCreateOrUpdate`), and Illinois ABP create (`facilityAbpFlatCreateOrUpdate`). Each schema defines its own `required` list, `state` rules, installer vs owner contact requirements, and the `panels` / `inverters` item schema. properties: name: type: string example: Facility name maxLength: 80 i_am_owner: description: >- Whether the submitter is the generation attribute owner (`0` / `1`; string forms are coerced). example: 1 nullable: true oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' i_have_solar_production_meter: example: 1 nullable: true oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' facility_type: description: |- Possible values are: - RES (Residential) - COM (Commercial) - MUNI (Municipal) - CMTY (Community Solar) type: string example: RES enum: - RES - COM - MUNI - CMTY facility_size: description: >- Facility DC capacity (DC kW). Minimum 0.000001; values are multiples of 0.000001. type: number example: 5.657 minimum: 0.000001 is_battery_backup: example: 1 oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' default: 0 usable_energy: description: >- Required when `is_battery_backup` is `1`. Numeric strings are accepted. example: 2.36 nullable: true oneOf: - type: number minimum: 0.000001 - type: string street: type: string example: 7312 Random Street maxLength: 80 apartment_suite: type: string maxLength: 80 city: type: string example: Yorktown maxLength: 80 county: type: string example: York County maxLength: 80 zip: type: string example: '24567' system_leased: example: 0 oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' default: 0 referrer_company: type: string example: Referrer company name maxLength: 100 owner_first_name: type: string example: John owner_last_name: type: string example: Doe owner_company_name: type: string example: Owner company name owner_email: type: string example: owner@email.com maxLength: 100 owner_street: type: string example: 7312 Random Street maxLength: 80 owner_apartment_suite: type: string maxLength: 80 owner_city: type: string example: Yorktown maxLength: 80 owner_state: type: string example: VA owner_zip: type: string example: '23693' system_cost: description: >- Optional for DGG creates; required for standard create. Numeric strings are accepted. example: 12345 nullable: true oneOf: - type: integer minimum: 1 - type: string meter_manufacturer: type: string example: AstroPower meter_model_type: type: string example: GTY-7676 meter_accuracy: description: |- - 'revenue' if the meter is a Revenue Grade Meter - 'standard' if the meter is not a Revenue Grade Meter type: string example: revenue enum: - revenue - standard meter_serial_number: type: string example: '12345' maxLength: 50 meter_remote_data_collector: type: string example: Enphase online_monitoring_api_id: type: string example: '123456' operation_start_date: description: >- Optional operation start date (YYYY-MM-DD or legacy date string accepted by the server). type: string example: '2018-10-17' utility_interconnection_date: description: Utility interconnection date (YYYY-MM-DD or legacy date string). type: string example: '2018-10-17' reading_at_interconnection: description: >- Often `0` for new interconnects; optional unless your program requires it. type: number example: 39.95 minimum: 0 comment_box: type: string utility_name: description: >- Should be a valid utility for the site county; must match `GET /utilities` for DGG payloads. type: string inverters: type: array items: $ref: '#/components/schemas/inverterFlatCreate' type: object facilityFlatCreateOrUpdate: description: Standard facility create payload. allOf: - $ref: '#/components/schemas/facilityFlatCreateOrUpdateCommonProperties' - $ref: '#/components/schemas/FacilityCreateStandardFields' ManualMeterReadingRequest: description: Manual meter reading submission body for `submitMeterReading`. required: - meter_reading - meter_reading_date_taken properties: meter_reading: description: The meter reading value provided by the user type: number example: 98765 minimum: 0 meter_reading_date_taken: description: >- Date when the meter reading was taken (YYYY-MM-DD). Required for cumulative meters. type: string format: date example: '2025-01-15' type: object ProcessMeterReadingImageRequest: description: >- Multipart body for `POST /facilities/{facility_id}/generation/meters/{meter_id}/image-readings`. required: - image properties: image: description: Meter reading photo type: string format: binary auto_submit_reading: description: Whether to automatically submit the reading after processing type: boolean default: false meter_reading_date_taken: description: >- Date when the meter reading was taken (required for cumulative meters) type: string format: date example: '2025-12-02' type: object StateCertificationInput: required: - state - state_certification_number properties: state: type: string example: PA maxLength: 2 state_certification_number: type: string example: PA-1234567-SUN-I type: object UploadFacilityDocumentsRequest: required: - documents properties: state: description: >- Optional two-letter state abbreviation. When omitted, documents are matched to all eligible facility states. type: string example: OH maxLength: 2 documents: type: array items: required: - document_name - document_content properties: document_name: description: >- Required document slug from the facility state eligibility response. type: string example: schedule_a document_content: description: Document file content. type: string format: binary type: object minItems: 1 type: object example: state: OH documents: - document_name: photo_of_solar_meter document_content: file content - document_name: schedule_a document_content: file content postAbpInverterCreate: description: >- Inverter array entry for ABP facility creation (`facilityAbpFlatCreateOrUpdate.inverters`). required: - number_of_inverters - inverter_capacity_kw - inverter_manufacturer - inverter_model_type - inverter_efficiency properties: number_of_inverters: type: number example: 5 inverter_capacity_kw: type: number example: 4.6 inverter_manufacturer: type: string example: SunPower inverter_model_type: type: string example: SPR-4600 (4600W (208Vac) Utility Interactive Inverter) inverter_efficiency: description: Inverter efficiency percentage (e.g. `97.5`). type: number example: 97.5 type: object postAbpPanelCreate: description: >- Panel array entry for ABP facility creation (`facilityAbpFlatCreateOrUpdate.panels`). required: - number_of_panels - panel_location - panel_rating_dc - panel_manufacturer - panel_model_type - tracking - array_tilt - array_orientation_azimuth - panels_bifacial properties: number_of_panels: type: number example: 20 panel_location: type: string example: Roof enum: - Ground - Roof panel_rating_dc: description: Wattage of each module in W (DC). type: number example: 400 panel_manufacturer: type: string example: Solartech Energy panel_model_type: type: string example: ASC-6M-72-295-3BB (295W Monocrystalline Module) tracking: type: string example: Fixed enum: - Fixed - '1' - '2' array_tilt: type: number example: 22 array_orientation_azimuth: type: number example: 180 panels_bifacial: description: Whether the panel array is bifacial. example: 0 oneOf: - type: integer enum: - 0 - 1 - type: string enum: - '0' - '1' - type: boolean type: object facilityAddressInput: required: - street - city - county - state - zip properties: street: type: string example: 7312 Random Street maxLength: 80 city: type: string example: Yorktown maxLength: 80 county: type: string example: York County maxLength: 80 state: type: string example: VA zip: type: string example: '23693' apartment: type: string maxLength: 80 type: object postDggPanelCreate: description: >- Panel array entry for DGG facility creation (`facilityDggFlatCreateOrUpdate.panels`). `panel_location` is optional (unlike `panelFlatCreate`). required: - number_of_panels - panel_rating_dc - panel_manufacturer - panel_model_type - tracking - array_tilt - array_orientation_azimuth properties: number_of_panels: type: number example: 20 panel_rating_dc: description: Wattage of each module in W (DC); up to 3 decimal places. type: number example: 400 panel_manufacturer: type: string example: Solartech Energy panel_model_type: type: string example: ASC-6M-72-295-3BB (295W Monocrystalline Module) tracking: description: >- Fixed for stationary arrays; `1` single-axis tracking; `2` dual-axis tracking. type: string example: Fixed enum: - Fixed - '1' - '2' array_tilt: description: Degrees (0–90); numeric strings are accepted. type: number example: 22 array_orientation_azimuth: description: Degrees (0–359.99); numeric strings are accepted. type: number example: 180 panel_location: type: string example: Roof enum: - Ground - Roof type: object facilityInstallerInput: required: - company properties: company: type: string example: Installer company name maxLength: 100 first_name: type: string example: John maxLength: 80 last_name: type: string example: Doe maxLength: 80 email: type: string example: contact@email.com maxLength: 100 phone: type: string example: 123-123-1234 address: $ref: '#/components/schemas/facilityAddressInput' type: object inverterFlatCreate: required: - number_of_inverters - inverter_capacity_kw - inverter_manufacturer - inverter_model_type properties: number_of_inverters: type: number example: 5 inverter_capacity_kw: type: number example: 4.6 inverter_manufacturer: type: string example: SunPower inverter_model_type: type: string example: SPR-4600 (4600W (208Vac) Utility Interactive Inverter) type: object facilityInverterInput: required: - number_of_inverters - capacity_kw - manufacturer - model_type properties: number_of_inverters: type: number example: 5 capacity_kw: type: number example: 4.6 manufacturer: type: string example: SunPower model_type: type: string example: SPR-4600 (4600W (208Vac) Utility Interactive Inverter) efficiency: description: >- Inverter efficiency percentage (e.g. `97.5`). Required for Illinois ABP facilities. type: number example: 97.5 type: object facilityMeterInput: required: - manufacturer - model_type - utility_interconnection_date - utility_name - utility_account_number properties: manufacturer: type: string example: AstroPower model_type: type: string example: GTY-7676 utility_interconnection_date: description: Utility interconnection date (YYYY-MM-DD or legacy date string). type: string example: '2018-10-17' utility_name: description: >- Should be a valid utility for the site county; must match `GET /utilities` for DGG payloads. type: string utility_account_number: type: string maxLength: 50 accuracy: description: |- - 'revenue' if the meter is a Revenue Grade Meter - 'standard' if the meter is not a Revenue Grade Meter type: string example: revenue enum: - revenue - standard serial_number: type: string example: '12345' maxLength: 50 last_date_certification: type: string example: '2016-08-02' remote_data_collector: type: string example: Enphase online_monitoring_api_id: type: string example: '123456' operation_start: description: >- Optional operation start date (YYYY-MM-DD or legacy date string accepted by the server). type: string example: '2018-10-17' reading_at_interconnection: description: >- Often `0` for new interconnects; optional unless your program requires it. type: number example: 39.95 minimum: 0 current_production_reading: type: number example: 39.95 minimum: 0 date_of_production_reading: type: string example: '2018-10-17' type: object facilityOwnerInput: required: - first_name - last_name - phone - address properties: first_name: type: string example: John last_name: type: string example: Doe phone: type: string example: 122-223-3321 address: $ref: '#/components/schemas/facilityAddressInput' email: type: string example: owner@email.com maxLength: 100 company_name: type: string example: Owner company name type: object panelFlatCreate: required: - number_of_panels - panel_location - panel_rating_dc - panel_manufacturer - panel_model_type - tracking - array_tilt - array_orientation_azimuth properties: number_of_panels: type: number example: 5 panel_location: type: string example: Roof enum: - Ground - Roof panel_rating_dc: type: number example: 240 panel_manufacturer: type: string example: Solartech Energy panel_model_type: type: string example: ASC-6M-72-295-3BB (295W Monocrystalline Module) tracking: type: string example: '1' enum: - Fixed - '1' - '2' array_tilt: type: number example: 30 array_orientation_azimuth: type: number example: 35 type: object facilityPanelInput: required: - number_of_panels - rating_dc - manufacturer - model_type - tracking - array_tilt - array_orientation_azimuth properties: number_of_panels: type: number example: 5 rating_dc: type: number example: 240 manufacturer: type: string example: Solartech Energy model_type: type: string example: ASC-6M-72-295-3BB (295W Monocrystalline Module) tracking: type: string example: '1' enum: - Fixed - '1' - '2' array_tilt: type: number example: 30 array_orientation_azimuth: type: number example: 35 location: description: Required for standard facilities; optional for DGG (California). type: string example: Roof enum: - Ground - Roof bifacial: description: >- Whether the panel array is bifacial. Required for Illinois ABP facilities. type: boolean example: false type: object facilityRegistryInput: description: >- Registry fields accepted on create/update. The registry name is derived from the facility state and cannot be set or updated. properties: registry_id_number: type: string example: NON12345 maxLength: 10 minLength: 4 type: object AbpFacilityDetails: description: >- Facility detail for an Illinois Adjustable Block Program (ABP) system. Returned from `GET /facilities/{id}?data=all` when the facility has an ABP application. Program access: Illinois ABP via API is limited to authorized accounts; contact our support team to enroll. allOf: - $ref: '#/components/schemas/FacilityDetails' - $ref: '#/components/schemas/AbpFacilityDetailsFields' AbpFacilityDetailsFields: description: Illinois ABP-specific fields included on facility detail responses. properties: icc_docket_number: description: Illinois Commerce Commission docket number. type: string nullable: true install_contract_exec_date: description: Installation contract execution date. type: string format: date nullable: true is_expansion: description: Whether this is an expansion application. type: boolean nullable: true existing_project_application_id: description: Prior ABP application ID when this is an expansion. type: string nullable: true pv_system_disclosure_form_id: description: Illinois PV System Disclosure Form identifier. type: string nullable: true rec_estimate_methodology: description: REC estimate methodology (`PVWATTS` or `CUSTOM_CAPACITY_FACTOR`). type: string nullable: true custom_capacity_factor: description: Custom capacity factor as a decimal. type: number format: float nullable: true custom_capacity_explanation: description: Explanation for a custom capacity factor. type: string nullable: true facility_energized: description: Whether the facility is energized. type: boolean nullable: true net_metering_approved: description: Whether net metering is approved. type: boolean nullable: true financial_structure: description: Financing structure (`Customer-owned`, `Lease`, or `PPA`). type: string nullable: true is_already_completed_project_app_on_the_same_parcel_of_land: description: Whether a prior project application exists on the same parcel. type: boolean nullable: true is_project_located_on_public_school_owned_land: description: Whether the site is on public school-owned land. type: boolean nullable: true ground_cover_ratio: description: Ground cover ratio. type: number format: float nullable: true prevailing_wage_subject: description: Prevailing wage applicability. type: boolean nullable: true minimal_shading_criteria: description: Whether minimal shading criteria are met. type: boolean nullable: true qualified_installer_person: description: Qualified installer contact name. type: string nullable: true construction_activities_completion_date: description: Construction completion date. type: string format: date nullable: true installer_demographics: oneOf: - $ref: '#/components/schemas/InstallerDemographics' nullable: true description: Installer workforce demographics. type: object DggFacilityDetails: allOf: - $ref: '#/components/schemas/FacilityDetails' - $ref: '#/components/schemas/DggFacilityDetailsFields' DggFacilityDetailsFields: properties: group_name: type: string nullable: true qualified_reporting_entity: type: string nullable: true qualified_reporting_entity_id_value: type: string nullable: true ac_facility_size: type: number format: float nullable: true can_provide_solar_production_data_access: type: boolean nullable: true type: object FacilitiesListForAccountList: properties: data: type: array items: $ref: '#/components/schemas/FacilityListDetails' type: object FacilityCompleteSuccess: description: Facility application completed successfully. required: - status properties: status: type: string example: success type: object facilityFlatCreateOrUpdateResponse200: description: >- Facility create/update success payload returned in the API envelope `data` field. required: - status - data properties: status: type: string example: success data: description: >- Created or updated facility detail object (same shape as a GET facility row). oneOf: - $ref: '#/components/schemas/AbpFacilityDetails' - $ref: '#/components/schemas/DggFacilityDetails' - $ref: '#/components/schemas/FacilityDetails' type: object FacilityDetails: description: Single facility detail object returned in FacilityResponse. properties: facility_id: type: integer example: 130235 name: type: string example: Smith Residence nullable: true energy_type_id: type: integer example: 1 nullable: true energy_type: type: string example: Solar Photovoltaic nullable: true facility_size: type: number format: float example: 1.5713 nullable: true facility_type: type: string example: RES nullable: true facility_type_long: type: string example: Residential nullable: true usable_energy: type: number format: float example: 12.34 system_cost: type: number format: float example: 12345 nullable: true is_battery_backup: type: boolean example: false system_leased: type: boolean example: false leasing_company: type: string example: Lease provider nullable: true referrer_company: type: string example: Solar Power nullable: true independent_verifier: type: string example: Verifier Inc nullable: true hostname: type: string nullable: true external_id: type: string example: EXT-1234 nullable: true i_am_owner: type: boolean example: true nullable: true has_solar_production_meter: type: boolean example: true nullable: true building_type: type: string example: Commercial nullable: true facility_location_type: type: string example: Rooftop nullable: true aggregate_net_metering: type: boolean example: false nullable: true colocated_facility: type: boolean example: false nullable: true cmty_application_number: type: string example: CMTY-000123 nullable: true operation_start_date: description: >- Commissioning / operation start date (locations.operation_start_date). type: string format: date example: '2024-03-01' nullable: true registry: $ref: '#/components/schemas/Registry' address: $ref: '#/components/schemas/Address' owner: $ref: '#/components/schemas/Owner' installer: $ref: '#/components/schemas/Installer' panels: type: array items: $ref: '#/components/schemas/Panel' inverters: type: array items: $ref: '#/components/schemas/Inverter' meter: $ref: '#/components/schemas/Meter' state_certifications: type: array items: $ref: '#/components/schemas/StateCertification' type: object FacilityDocumentsUploadResult: description: >- Upload counts and error messages inside {@see FacilityDocumentsUploadSuccess}. required: - uploaded - errors properties: uploaded: type: integer example: 3 errors: type: array items: type: string example: [] type: object uploadFacilityDocumentsResponse: description: Document upload result returned in the API envelope `data` field. required: - status - data properties: data: $ref: '#/components/schemas/FacilityDocumentsUploadResult' status: type: string example: success type: object FacilityListDetails: description: Facility summary returned in the account facilities list. properties: facility_id: type: integer example: 130235 name: type: string example: test API facility name nullable: true energy_type_id: type: integer example: 1 nullable: true energy_type: type: string example: Solar Photovoltaic nullable: true facility_size: type: number format: float example: 1.5713 nullable: true facility_type: type: string example: RES nullable: true facility_type_long: type: string example: Residential nullable: true usable_energy: type: number format: float example: 12.34 system_cost: type: number format: float example: 12345 nullable: true is_battery_backup: type: boolean example: false system_leased: type: boolean example: false referrer_company: type: string example: test - Bayer, Jast and Champlin nullable: true ticket_owner: description: May be empty when unset. type: string example: '' ticket_owner_user_id: type: integer example: 42 nullable: true independent_verifier: type: string example: Verifier Inc nullable: true operation_start_date: description: >- Commissioning / operation start date (locations.operation_start_date). type: string format: date example: '2024-03-01' nullable: true registry: $ref: '#/components/schemas/Registry' address: $ref: '#/components/schemas/Address' owner: $ref: '#/components/schemas/Owner' installer: $ref: '#/components/schemas/Installer' meter: $ref: '#/components/schemas/Meter' type: object FacilityResponse: description: >- Facility detail rows returned in the API envelope `data` field (single-facility GET returns one element). type: array items: oneOf: - $ref: '#/components/schemas/AbpFacilityDetails' - $ref: '#/components/schemas/DggFacilityDetails' - $ref: '#/components/schemas/FacilityDetails' FacilityStateEligibilitiesList: description: State eligibility rows in the API envelope `data` field. type: array items: $ref: '#/components/schemas/StateEligibilityItem' FacilityTransactionsPaginatedResponse: properties: data: $ref: '#/components/schemas/TransactionReportData' type: object MeterReadingEligibilityResult: description: Whether the facility/meter can submit manual readings at this time required: - can_submit - code - message properties: submission_window: $ref: '#/components/schemas/MeterReadingSubmissionWindow' last_submission: $ref: '#/components/schemas/MeterReadingLastSubmission' can_submit: description: Whether the facility/meter can submit manual readings at this time type: boolean example: true code: description: Status code when `can_submit` is false (null when eligible) type: string nullable: true enum: - MULTIPLE_FACILITIES - NO_FACILITY - FIRST_TIME_SUBMISSION - CUMULATIVE_ONLY - AUTO_REPORTING - METER_NOT_VERIFIED - NEPOOL_REGISTRY - WREGIS_REGISTRY - NOT_ELIGIBLE - NO_METER - OUTSIDE_SUBMISSION_WINDOW message: description: Human-readable message explaining submission status type: string nullable: true type: object MeterReadingImageExtractedData: description: OCR extracted data from the meter reading image required: - meter_brand - meter_model_number - meter_reading - raw_reading - is_valid - error_message properties: meter_brand: description: Extracted meter brand name type: string example: '' meter_model_number: description: Extracted meter model number type: string example: '' meter_reading: description: Extracted meter reading value type: string example: '' raw_reading: description: Raw OCR text before processing type: string example: '' is_valid: description: Whether the extracted reading is considered valid type: boolean example: false error_message: description: Error message if extraction failed or validation failed type: string example: '' type: object MeterReadingImageProcessingStatus: description: OCR processing status and extracted data required: - success - status - requires_manual_entry - remaining_attempts - extracted_data properties: extracted_data: $ref: '#/components/schemas/MeterReadingImageExtractedData' success: description: Whether the request was successful type: boolean example: true status: description: Current OCR processing status type: string example: processing enum: - pending - processing - successful - failed requires_manual_entry: description: >- Whether manual entry is required (true when status is failed and no remaining attempts) type: boolean example: false remaining_attempts: description: >- Number of remaining photo submission attempts before manual entry is required type: integer example: 5 maximum: 5 minimum: 0 type: object MeterReadingLastSubmission: required: - meter_readings_id - generation_period - value properties: meter_readings_id: description: ID of the last meter reading submission type: integer example: 4728475 generation_period: description: Period for which the last meter reading was submitted type: string example: 2025/09 user_reading_taken: description: Date on which the user has taken the reading type: string format: date example: '2025-10-12' nullable: true value: description: Value of the last meter reading type: number example: 3701 type: object MeterReadingSubmissionPeriod: required: - start - end properties: start: description: Start date of the submission period type: string format: date example: '2025-11-01' end: description: End date of the submission period type: string format: date example: '2025-11-02' type: object MeterReadingSubmissionWindow: properties: current_period: $ref: '#/components/schemas/MeterReadingSubmissionPeriod' next_period: $ref: '#/components/schemas/MeterReadingSubmissionPeriod' type: object ProcessMeterReadingImageSuccess: description: Image accepted for OCR processing. required: - success - message - processing_id - remaining_attempts properties: success: description: Indicates if the request was successful type: boolean example: true message: description: Human-readable success message type: string example: Meter readings processing started processing_id: description: Unique identifier for tracking the image processing status type: integer example: 16 remaining_attempts: description: >- Number of remaining photo submission attempts before manual entry is required type: integer example: 4 maximum: 5 minimum: 0 type: object ProductionHistoryData: description: >- Production history payload in the API envelope `data` field for `getProductionHistory`. required: - meter_id - meter_reading_style - current_reading_value - current_reading_status - current_reading_period - last_approved_photo_url - last_approved_photo_description - last_approved_photo_date - history properties: meter_id: description: ID of the meter type: integer example: 73059 meter_reading_style: description: Style of meter reading (cumulative, actual, etc.) type: string example: cumulative current_reading_value: description: Current meter reading value type: integer example: 73332 current_reading_status: description: Status of the current reading type: string example: srectrade_approved nullable: true enum: - srectrade_approved - high - low - reading_less_than_previous - pending_review - srectrade_disapproved - setup_incomplete - date_outside_expected - wrong_held_gen_id - missing_previous_verified - extrapolation_off - invalid_meter_style - already_processed current_reading_period: description: Period of the current reading in YYYY-MM-DD format type: string example: '2025-10-01' nullable: true last_approved_photo_url: description: URL of the last approved photo submission type: string nullable: true last_approved_photo_description: description: Description of the last approved photo submission type: string example: First time reading nullable: true last_approved_photo_date: description: Date of the last approved photo submission in YYYY-MM-DD format type: string format: date example: '2025-02-01' nullable: true history: description: Array of production history records type: array items: $ref: '#/components/schemas/ProductionHistoryItem' type: object ProductionHistoryItem: description: Single production history row in `ProductionHistoryData.history`. required: - meter_readings_id - generation_period properties: meter_readings_id: description: Unique identifier for the meter reading type: integer example: 4742721 generation_period: description: Generation period in YYYY/MM format type: string example: 2025/10 reading_date: description: Date when the reading was taken; omitted when unknown type: string format: date nullable: true reading_status: description: Status of the reading type: string nullable: true enum: - srectrade_approved - high - low - reading_less_than_previous - pending_review - srectrade_disapproved - setup_incomplete - date_outside_expected - wrong_held_gen_id - missing_previous_verified - extrapolation_off - invalid_meter_style - already_processed production: description: Production value for the period type: number format: float example: 547 nullable: true reading_value: description: The actual meter reading value type: number format: float example: 73332 nullable: true info: description: Additional information about the reading type: string example: Reading is waiting to be verified by your tracking registry. nullable: true photo_url: description: URL of the photo submission for this reading type: string nullable: true photo_description: description: Description of the photo submission for this reading type: string example: First time reading nullable: true type: object ProductionHistoryPaginatedResponse: properties: data: $ref: '#/components/schemas/ProductionHistoryData' type: object StateApplicationCreateData: description: >- State application create success payload returned in the API envelope `data` field. required: - status - data properties: status: type: string example: success data: required: - application_id properties: application_id: type: integer example: 12345 type: object type: object StateCertificationCreateData: description: >- State certification create success payload returned in the API envelope `data` field. required: - status - data properties: status: type: string example: success data: description: Empty array on successful certification create. type: array items: {} example: [] type: object StateEligibilityDocument: description: |- Required document row for a state eligibility entry. Runtime: {@see \Trade\CodeIgniter\models\dto\Document_dto}. required: - name - required - filetype properties: name: type: string example: Photo of Solar Meter required: type: boolean example: true filetype: type: string example: png|gif|jpg|pdf|jpeg type: object StateEligibilityItem: required: - state - is_eligible - product_type properties: documents: description: >- One state's eligibility details returned in the API envelope `data` array. Runtime: {@see \Trade\CodeIgniter\models\dto\Eligible_state_dto::jsonSerialize()}. type: array items: $ref: '#/components/schemas/StateEligibilityDocument' errors: description: Validation messages when the state is not eligible type: array items: type: string nullable: true state: type: string example: PA is_eligible: type: boolean example: true product_type: type: string example: Tier-I type: object MeterReadingSuccessResponse: description: Success response payload (inside the data envelope) required: - success - meter_readings_id - meter_reading_status properties: success: description: Indicates if the submission was successful type: boolean example: true meter_readings_id: description: The unique identifier of the created/updated meter reading type: integer example: 54321 meter_reading_status: description: The status of the submitted meter reading type: string example: approved enum: - approved - held_high - held_low - held_less_than_previous - pending_review - disapproved - setup_incomplete - date_outside_expected - wrong_held_gen_id - missing_previous_verified - extrapolation_off - invalid_meter_style - already_processed - unknown message: description: Success message (optional) type: string example: Meter reading submitted successfully type: object SubmitReadingVerificationSuccess: description: Verification submission accepted — reading moved to `pending_review`. required: - meter_readings_id - meter_readings_status - message properties: meter_readings_id: description: ID of the meter reading that was verified. type: integer example: 98765 meter_readings_status: description: >- New status of the reading after verification. Always `pending_review`. type: string example: pending_review enum: - pending_review message: description: Human-readable confirmation message. type: string example: Verification submitted successfully type: object TransactionItem: description: Single transaction row in `TransactionReportData.transactions`. required: - date - net_sales - status - quantity - product properties: date: description: Transaction date in YYYY-MM-DD format type: string example: '2025-07-24' net_sales: description: Net sales amount for the transaction type: number format: float example: 123.45 status: description: Payment status of the transaction type: string example: Not yet initiated quantity: description: Quantity of RECs/SRECs sold type: number format: float example: 5 generation: description: Month of generation for the transaction in YYYY/MM format type: string example: 2025/05 nullable: true product: description: Product name/type type: string example: VA2025-SREC type: object TransactionReportData: description: >- Transaction report payload in the API envelope `data` field for `getTransactionsHistory`. required: - total_earned - available_recs - transactions properties: total_earned: description: Total amount earned from all transactions type: number format: float example: 369.65 last_payment_date: description: Date of the most recent payment type: string format: date example: '2025-07-24' nullable: true available_recs: description: Number of available RECs/SRECs type: integer example: 3 transactions: description: List of transactions for the current page type: array items: $ref: '#/components/schemas/TransactionItem' type: object Address: properties: street: type: string example: 492 Darby Rd nullable: true apartment: type: string nullable: true city: type: string example: Yorktown nullable: true county: type: string nullable: true state: type: string example: VA nullable: true zip: type: string example: '23693' nullable: true type: object Installer: properties: company: type: string example: test - Conn - Kulas nullable: true first_name: type: string example: Leola nullable: true last_name: type: string example: Doe nullable: true email: type: string example: Josue59@example.com nullable: true phone: type: string example: 987-223-7611 nullable: true address: $ref: '#/components/schemas/Address' type: object Inverter: properties: id: type: integer example: 107060 number_of_inverters: type: integer example: 2 nullable: true manufacturer: type: string example: Agepower Limit nullable: true model_type: type: string nullable: true inverter_capacity_kw: type: number format: float example: 2.23 nullable: true type: object Meter: properties: id: type: integer example: 113521 nullable: true accuracy: type: string example: standard nullable: true serial_number: type: string example: SE704 nullable: true model_type: type: string example: Test nullable: true manufacturer: type: string example: Test2 nullable: true operation_start: type: string format: date example: '2024-03-01' nullable: true last_date_certification: type: string format: date example: '2024-02-10' nullable: true utility_name: type: string example: Virginia Electric & Power Co nullable: true utility_id: type: integer example: 56810 nullable: true utility_account_number: type: string example: '234567' nullable: true reading_at_interconnection: type: number format: float example: 2 nullable: true utility_interconnection_date: type: string format: date example: '2024-03-01' nullable: true current_production_reading: type: number format: float example: 12.321 nullable: true date_of_production_reading: type: string format: date example: '2024-05-14' nullable: true remote_data_collector: type: string example: enphase nullable: true remote_data_collector_id: type: string example: '61' nullable: true online_monitoring_api_id: type: string example: '234567' nullable: true type: object Owner: properties: first_name: type: string example: Isaac nullable: true last_name: type: string example: Doe nullable: true email: type: string example: test@example.com nullable: true phone: type: string example: 122-223-3321 nullable: true company_name: type: string example: test name Towne Inc nullable: true address: $ref: '#/components/schemas/Address' type: object Panel: properties: array_id: type: integer example: 189624 location: type: string example: Roof nullable: true tracking: type: string example: Fixed nullable: true array_orientation_azimuth: type: number format: float example: 320 nullable: true array_tilt: type: number format: float example: 80 nullable: true number_of_panels: type: integer example: 3 nullable: true manufacturer: type: string nullable: true model_type: type: string example: test nullable: true rating_dc: type: number format: float example: 230.1 nullable: true type: object Registry: properties: registry: type: string example: GATS nullable: true registry_id_number: type: string example: NON00220 nullable: true type: object StateCertification: properties: id: type: integer example: 181904 state_abbreviation: type: string example: DC certification_number: type: string example: DC-426425-SUN-I nullable: true type: object InstallerDemographics: description: >- Installer workforce demographics for facility create/update requests and ABP detail responses. properties: hours_race_white: description: Total hours worked by White employees. type: number format: float nullable: true hours_race_black_or_aa: description: Total hours worked by Black or African American employees. type: number format: float nullable: true hours_race_ai_or_an: description: Total hours worked by American Indian or Alaskan Native employees. type: number format: float nullable: true hours_race_asian: description: Total hours worked by Asian employees. type: number format: float nullable: true hours_race_h_or_pi: description: Total hours worked by Hawaiian or other Pacific Islander employees. type: number format: float nullable: true hours_race_multi: description: Total hours worked by employees of More Than One Race. type: number format: float nullable: true hours_race_other: description: Total hours worked by employees of Some Other Race. type: number format: float nullable: true hours_race_decline: description: Total hours worked by employees who decline to identify race. type: number format: float nullable: true hours_eth_hispanic_latino: description: Total hours worked by Hispanic or Latino employees. type: number format: float nullable: true hours_eth_non_hispanic_latino: description: Total hours worked by Not Hispanic or Latino employees. type: number format: float nullable: true hours_eth_decline: description: Total hours worked by employees who decline to identify ethnicity. type: number format: float nullable: true training_solar: description: Solar Training Pipeline Program (FEJA program) hours. type: number format: float nullable: true training_craft: description: Craft Apprenticeship Program (FEJA program) hours. type: number format: float nullable: true training_multi_cult: description: Multi-Cultural Job Training Program (FEJA program) hours. type: number format: float nullable: true type: object UploadErrorPreviewClientError: description: >- Shared `{success, error}` shape for upload error preview client failures (400/403). properties: success: type: boolean enum: - false error: type: string type: object UploadErrorPreviewForbiddenError: description: 403 `data` payload when the caller cannot access the upload. type: object allOf: - $ref: '#/components/schemas/UploadErrorPreviewClientError' - properties: success: type: boolean enum: - false error: type: string example: You do not have permission to access this upload. type: object UploadErrorPreviewInvalidErrorIndexError: description: >- 400 `data` payload when `error_index` query parameter is not a valid integer. type: object allOf: - $ref: '#/components/schemas/UploadErrorPreviewClientError' - properties: success: type: boolean enum: - false error: type: string example: Invalid error_index value. type: object UploadErrorPreviewNotFoundError: description: 404 `data` payload when no error group or fixer matches the request. properties: success: type: boolean enum: - false message: type: string example: Error fix or error fixer not found error: description: Machine-readable error code when present. type: string example: not_found nullable: true type: object FixPreviewPayload: required: - fixer_key - field - changes properties: changes: description: One entry per affected upload row type: array items: $ref: '#/components/schemas/PreviewChangeRow' fixer_key: description: Identifier of the fixer used for this preview type: string example: manual_correction field: description: Logical field name being corrected type: string example: street scope: type: string example: facilities nullable: true field_path: description: Dot-path of the field within the scope type: string example: facilities.street nullable: true type: object PreviewChangeRow: description: >- One proposed change row in a fix preview (`FixPreviewPayload.changes[]`). required: - row_index - old_value - new_value properties: row_index: description: Upload row identifier (internal row id) type: integer example: 7050928 old_value: description: Current value before fix (type may vary by field) type: string nullable: true new_value: description: Value after applying the proposed fix type: string nullable: true type: object UploadBulkErrorPreviewSuccess: required: - success - affected_row_ids properties: affected_row_ids: $ref: '#/components/schemas/FixPreviewPayload' success: type: boolean enum: - true type: object UploadSingleRowErrorPreviewSuccess: required: - success - affected_row_ids properties: affected_row_ids: $ref: '#/components/schemas/FixPreviewPayload' success: type: boolean enum: - true type: object AcceptInvitation: description: Payload to accept an invitation addressed to the caller. required: - invite_key properties: invite_key: description: The SHA-256 invite key delivered in the invitation email link. type: string example: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2 type: object CreateInvitation: description: Payload to create a new account invitation. required: - email - first_name - last_name - role_ids properties: email: type: string format: email example: invitee@example.com first_name: type: string example: Jane last_name: type: string example: Doe role_ids: description: Role IDs to grant. Must be a subset of {1, 2, 3} and must include 1. type: array items: type: integer enum: - 1 - 2 - 3 example: - 1 - 2 type: object UpdateInvitationPermissions: description: Replace the roles granted by a pending invitation. required: - role_ids properties: role_ids: description: Role IDs to grant. Must be a subset of {1, 2, 3} and must include 1. type: array items: type: integer enum: - 1 - 2 - 3 example: - 1 - 3 type: object AcceptInvitationResult: description: Result of accepting an invitation addressed to the caller. required: - invitation_id - account_id - membership_id - active_account_id properties: invitation_id: type: integer example: 42 account_id: type: integer example: 1001 membership_id: type: integer example: 555 nullable: true active_account_id: type: integer example: 1001 type: object Invitation: description: A pending account invitation and its assigned roles. required: - id - account_id - email - first_name - last_name - role_ids - invited_by - created - active properties: id: type: integer example: 42 account_id: type: integer example: 1001 account_name: type: string example: Acme Solar LLC nullable: true email: type: string format: email example: invitee@example.com first_name: type: string example: Jane last_name: type: string example: Doe role_ids: type: array items: type: integer example: - 1 - 2 invited_by: $ref: '#/components/schemas/InvitedBy' created: type: string format: date-time example: '2026-01-15T09:30:00-05:00' active: type: boolean example: true type: object InvitationList: description: A list of pending invitations. required: - invitations properties: invitations: type: array items: $ref: '#/components/schemas/Invitation' type: object InvitationPaginatedList: properties: data: $ref: '#/components/schemas/InvitationList' type: object InvitedBy: description: The user who created the invitation. required: - user_id - first_name - last_name properties: user_id: type: integer example: 7 first_name: type: string example: Owner nullable: true last_name: type: string example: User nullable: true type: object RemoveInvitationResult: description: Result of cancelling a pending invitation. required: - invitation_id - removed properties: invitation_id: type: integer example: 42 removed: type: boolean example: true type: object ResendInvitationResult: description: Result of re-sending an invitation email. required: - invitation_id - email_sent_to - template properties: invitation_id: type: integer example: 42 email_sent_to: type: string format: email example: invitee@example.com template: type: string example: not_user enum: - has_account - user_no_account - not_user type: object ManagedFacilityGroupWorkflowError: description: >- Error payload in the API envelope `data` field for workflow transition failures (400 / 404). required: - error - message properties: error: type: string example: invalid_transition message: type: string example: Group 7 cannot move from submitted to minted groups: description: Per-group failure details when one or more groups cannot transition. type: array items: $ref: '#/components/schemas/ManagedFacilityGroupWorkflowGroupFailure' type: object ManagedFacilityGroupWorkflowGroupFailure: description: >- Per-group failure detail in {@see ManagedFacilityGroupWorkflowError} `groups`. required: - group_id - current_status - reason properties: group_id: type: integer example: 7 minimum: 1 current_status: type: string example: submitted reason: type: string example: invalid_transition enum: - not_found - same_status - invalid_transition type: object AddManagedFacilityGroupMemberRequest: required: - facility_id properties: facility_id: description: facilities_id to attach type: integer type: object CreateManagedFacilityGroupRequest: required: - max_capacity_kw - utility_id - autoreporter_id properties: utility_id: description: >- Electric utility for the group (`managed_facility_groups.utility_id` → `utilities.utilities_id`). type: integer minimum: 1 autoreporter_id: description: >- Autoreporter for the group (`managed_facility_groups.autoreporter_id` → `remote_data_collectors.remote_data_collectors_id`). type: integer minimum: 1 name: description: >- Display name; if omitted or whitespace-only, a unique generated name is used. type: string max_capacity_kw: description: >- Maximum total allowed sum of member nameplate kW for the group (enforced against `facility_size_kw` when adding members). type: number format: float facility_ids: description: >- Optional initial members as `facilities_id` values. Must belong to the API account. Duplicates and non-positive IDs are ignored. type: array items: type: integer minimum: 1 type: object ManagedFacilityGroupWorkflowRequest: required: - group_ids - status properties: group_ids: description: >- Managed facility group IDs to transition (duplicates are ignored). Each entry may be a positive integer or numeric string. type: array items: oneOf: - type: integer example: 42 minimum: 1 - type: string pattern: ^[1-9][0-9]*$ example: '42' example: - 7 - 12 minItems: 1 status: description: >- Target workflow status. Must be the forward-only next step from each group's current status. type: string example: submitted enum: - admin_review - approved - complete - minted - submitted type: object ManagedFacilityGroupFacility: description: A facility that belongs to a managed facility group (summary row) properties: facility_id: type: integer example: 1001 name: type: string example: Main Street Solar capacity_kw: type: number format: float example: 250.5 address_line: type: string example: 123 Main St, City, ST 12345 nullable: true utility: description: Facility utility when set; empty object when none properties: id: type: integer name: type: string type: object auto_reporter: description: Facility auto-reporter when set; empty object when none properties: id: type: integer name: type: string type: object commencement_date: type: string example: '2023-06-15' nullable: true ticket_owner: description: May be empty when unset. type: string example: '' registry_id_number: type: string example: W12345 nullable: true registry_status: type: string example: Approved nullable: true type: object ManagedFacilityGroup: description: Managed facility group with nested facility summaries properties: id: type: integer example: 42 name: type: string example: Portfolio A status: type: string example: approved max_capacity_kw: description: Declared group capacity cap (kW DC) type: number format: float example: 500 facilities_count: type: integer example: 3 facilities: type: array items: $ref: '#/components/schemas/ManagedFacilityGroupFacility' utility: description: Group-level utility when configured; empty object when none properties: id: type: integer name: type: string type: object auto_reporter: description: Group-level auto-reporter when configured; empty object when none properties: id: type: integer name: type: string type: object group_real_capacity_kw: description: >- Sum of member `facility_size_kw` (nameplate DC kW); matches total of nested `capacity_kw`. type: number format: float example: 320.25 created_by: description: >- Display name of the user in `managed_facility_groups.created_by`. May be empty when unset. type: string example: Jane Q. Public commencement_date: description: Earliest member facility commencement date type: string format: date nullable: true type: object ManagedFacilityGroupAccountAccessItem: description: >- One account row in the API envelope `data` array for `getManagedFacilityGroupAccountAccess`. required: - account_id - account_name properties: account_id: description: Account identifier. type: integer account_name: description: Display name from the `account` table. type: string type: object ManagedFacilityGroupAccountAccessListResponse: description: >- Enabled managed facility group access rows returned in the API envelope `data` field. type: array items: $ref: '#/components/schemas/ManagedFacilityGroupAccountAccessItem' ManagedFacilityGroupAddMemberResult: description: Membership created when adding a facility to a group. required: - membership properties: membership: $ref: '#/components/schemas/ManagedFacilityGroupMembership' type: object ManagedFacilityGroupAddMemberSuccess: description: >- Success payload in the API envelope `data` field for add-member (HTTP 200). required: - status - data properties: status: type: string example: success data: $ref: '#/components/schemas/ManagedFacilityGroupAddMemberResult' type: object ManagedFacilityGroupCreateResult: description: Created group and optional initial memberships. required: - managed_facility_group properties: managed_facility_group: $ref: '#/components/schemas/ManagedFacilityGroupRecord' memberships: description: Present when the group was created with non-empty `facility_ids`. type: array items: $ref: '#/components/schemas/ManagedFacilityGroupMembership' type: object ManagedFacilityGroupMembership: description: Membership row in `managed_facility_groups_has_facilities`. properties: id: type: integer nullable: true managed_facility_group_id: type: integer example: 42 facility_id: type: integer example: 101 created_at: type: string nullable: true created_by: type: integer nullable: true updated_at: type: string nullable: true updated_by: type: integer nullable: true deleted_at: type: string nullable: true type: object ManagedFacilityGroupMutationSuccess: description: >- Success payload in the API envelope `data` field for managed facility group mutations. required: - status - data properties: status: type: string example: success data: $ref: '#/components/schemas/ManagedFacilityGroupCreateResult' type: object ManagedFacilityGroupList: properties: data: type: array items: $ref: '#/components/schemas/ManagedFacilityGroup' type: object ManagedFacilityGroupRecord: description: Managed facility group row returned in mutation success payloads. properties: id: type: integer example: 42 name: type: string example: Portfolio A owner_account_id: type: integer example: 4242 status: type: string example: admin_review enum: - admin_review - approved - complete - minted - submitted max_capacity_kw: type: number format: float example: 500 nullable: true utility_id: type: integer example: 12 nullable: true autoreporter_id: type: integer example: 5 nullable: true created_at: type: string nullable: true created_by: type: integer nullable: true updated_at: type: string nullable: true updated_by: type: integer nullable: true deleted_at: type: string nullable: true type: object ManagedFacilityGroupRemoveMemberResult: description: Outcome when a facility membership is removed from a group. required: - removed - facility_id properties: removed: type: boolean example: true facility_id: description: facilities_id that was removed type: integer example: 101 type: object ManagedFacilityGroupRemoveMemberSuccess: description: >- Success payload in the API envelope `data` field for remove-member (HTTP 200). required: - status - data properties: status: type: string example: success data: $ref: '#/components/schemas/ManagedFacilityGroupRemoveMemberResult' type: object ManagedFacilityGroupUngroupResult: description: >- Count of memberships soft-deleted when clearing a managed facility group. required: - removed_members properties: removed_members: description: Number of memberships soft-deleted for this group. type: integer example: 3 minimum: 0 type: object ManagedFacilityGroupUngroupSuccess: description: Success payload in the API envelope `data` field for ungroup (HTTP 200). required: - status - data properties: status: type: string example: success data: $ref: '#/components/schemas/ManagedFacilityGroupUngroupResult' type: object ManagedFacilityGroupWorkflowList: description: >- Updated managed facility groups after a workflow transition, returned in the API envelope `data` field. type: array items: $ref: '#/components/schemas/ManagedFacilityGroup' ManagedFacilityGroupWregis: description: >- Derived owner contact fields and interconnected utility from the first member facility of a managed facility group. properties: managed_facility_group_id: type: integer example: 42 owner_company_name: description: >- Owner contact company name from the first facility (`facility` → `owners` → `contacts.company_name`). type: string example: PEARLX VPP HOLDINGS, LLC nullable: true owner_address: description: >- Owner mailing address from the first facility in the group (`facility` → `owners` → `addresses`). Null when unavailable. properties: street: type: string example: 1279 STONEY CREEK RD nullable: true apartment_suite: type: string example: Suite 100 nullable: true city: type: string example: PASO ROBLES nullable: true county: type: string example: San Luis Obispo nullable: true state: type: string example: CA nullable: true zip: type: string example: '93446' nullable: true type: object nullable: true owner_email: description: >- Owner contact email from the first facility (`facility` → `owners` → `contacts.email`). type: string example: developers+devsite500249@srectrade.com nullable: true interconnected_utility: description: >- Interconnected utility from the group (`managed_facility_groups.utility_id` → `utilities`). Null when unavailable. properties: id: type: integer example: 56810 name: type: string example: Pacific Gas & Electric (PGE) type: object nullable: true engineering_capacity_factor_percent: description: >- Default engineering capacity factor percent for WREGIS display (numeric string without % suffix). type: string example: '35' reporting_entity: description: Default reporting entity label for WREGIS display. type: string example: Self Reporting country: description: Default country label for WREGIS display. type: string example: United States state_province: description: Default state/province label for WREGIS display. type: string example: California fuel_type: description: Default fuel type label for WREGIS display. type: string example: Solar fuel_sources: description: Default fuel source(s) label for WREGIS display. type: string example: Solar dg_group_type: description: Default DG group type label for WREGIS display. type: string example: Residential type: object MarketPricingItem: description: >- One market pricing row in the API envelope `data` array for `searchMarket`. required: - product - date properties: product: type: string example: PA2018 date: type: string example: '2017-10-20' bid_price: type: number format: float example: 5.5 nullable: true offer_price: type: number format: float example: 6.5 nullable: true type: object MarketPricingListResponse: description: Market pricing rows returned in the API envelope `data` field. type: array items: $ref: '#/components/schemas/MarketPricingItem' SubmitFeedback: description: Payload to submit mobile app feedback properties: rating: description: User satisfaction rating. type: string example: happy nullable: false enum: - bad - neutral - happy comment: description: Optional free-text comment (max 2000 characters). type: string example: Great app! nullable: true maxLength: 2000 type: object Feedback: description: Confirmation of a successfully submitted feedback entry. required: - id - date_created properties: id: description: ID of the created feedback record. type: integer example: 42 rating: description: User satisfaction rating. type: string example: happy nullable: true enum: - bad - neutral - happy comment: description: Optional free-text comment. type: string example: Great app! nullable: true date_created: description: Timestamp of the feedback submission. type: string example: '2026-06-04 12:00:00' type: object PartnerTermsVersionMismatchError: required: - error - message - expected_version - submitted_version type: object allOf: - $ref: '#/components/schemas/Error' - properties: error: description: >- POST `/partners/{partner_id}/terms-agreement` when `accepted_version` does not match the server config. type: string example: version_mismatch message: type: string example: >- The submitted terms version does not match the current server version. expected_version: description: Current partner T&C version on the server (wire format `v{N}`). type: string example: v2 submitted_version: description: '`accepted_version` from the request body.' type: string example: v1 type: object AcceptPartnerTermsRequest: description: >- Payload to record acceptance of the current partner Terms & Conditions version. required: - accepted_version properties: accepted_version: description: >- Version string the client is acknowledging. Must match the server's current partner T&C version (e.g. "v2"). type: string example: v2 minLength: 1 type: object CreatePartnerRequest: description: Payload to create a new partner required: - referrer_name - primary_user_email properties: referrer_name: description: Unique name that identifies the referrer / partner. type: string example: Solar Solutions Inc. nullable: false minLength: 1 primary_user_email: description: Email address of the primary user account for this partner. type: string format: email example: contact@solarsolutions.com nullable: false partner_pay: description: >- When true, pay commission immediately (same as Partner Admin checkbox). Defaults to null (inactive) when omitted. On create, partner_status is Active when true and Inactive when false or omitted. type: boolean example: true nullable: true disable_owner_popup_and_autoselect: description: >- When true, suppresses the owner-selection popup and auto-selects the owner. Defaults to false if omitted. type: boolean example: false nullable: false type: object PatchPartnerRequest: description: Partial partner update. Send only fields to change. properties: referrer_name: description: Partner / referrer display name. type: string example: Acme Solar minLength: 1 primary_user_email: description: Email of the primary user for the partnership. type: string format: email example: partner@example.com minLength: 1 partner_pay: description: >- When true, pay commission immediately (same as Partner Admin checkbox). type: boolean example: true nullable: true disable_owner_popup_and_autoselect: description: >- Disable ownership confirmation popup and auto-select "I am not the owner" on applications. When omitted, the existing database value is left unchanged (partial PATCH). type: boolean example: false nullable: true type: object CommissionRateItem: required: - type - percentage properties: type: description: Rate name type: string example: General enum: - General - ABP - LCFS percentage: description: Percentage as a numeric value (e.g. 18.5) type: number format: float example: 18.5 type: object CommissionRatesGetOk: description: Partner commission rates returned in the API envelope `data` field. required: - commission_rates properties: commission_rates: description: >- List of commission rate items (type + percentage). Empty array when the partner has no commission rate rows. type: array items: $ref: '#/components/schemas/CommissionRateItem' type: object Partner: description: Partner object properties: partner_id: type: integer example: 123 referrer_name: type: string example: solar nullable: true partner_pay: type: boolean example: true nullable: true partner_status: description: Partner activity status. type: string example: Active enum: - Active - Inactive partner_created: type: string example: '2026-01-01 00:00:00' nullable: true partner_primary_account: type: integer example: 456 nullable: true partner_primary_user: type: integer example: 789 nullable: true disable_owner_popup_and_autoselect: type: boolean example: false profile: oneOf: - $ref: '#/components/schemas/PartnerProfile' nullable: true type: object commission_rates: description: >- Commission rate items (type + percentage). Included on single-partner GET. type: array items: $ref: '#/components/schemas/CommissionRateItem' nullable: true type: object PartnerList: properties: data: type: array items: $ref: '#/components/schemas/Partner' type: object PartnerProfile: description: Partner profile object properties: first_name: type: string example: John nullable: true last_name: type: string example: Doe nullable: true email: type: string example: john.doe@example.com nullable: true type: object PartnerStatusLog: required: - date - action - initiated_by properties: date: description: >- Status change timestamp returned by the legacy API. Clients format for display. type: string example: '2024-09-06 15:33:04' nullable: true action: description: >- Human-readable status change label (matches Partner Admin Status History). type: string example: Partner status set to Active initiated_by: description: Full name of the user who initiated the change. type: string example: Jane Doe type: object PartnerStatusLogList: properties: data: type: array items: $ref: '#/components/schemas/PartnerStatusLog' type: object PartnerTag: description: Auto-assigned referrer tag for a partner (Partner Admin alignment). properties: tag_type: description: Tag type name (uppercase). type: string example: CONTRACT tag_name: type: string example: North Region date_referrer_tagged: description: Date portion of tagging (YYYY-MM-DD). type: string format: date example: '2024-06-01' history: description: 'Latest status history row: timestamp, action label, initiator.' properties: date: description: History created timestamp in app timezone (Y-m-d H:i:s T). type: string example: 2024-06-01 13:30:45 EDT action: type: string example: Referrer tag set to Active initiated_by: type: string example: Jane Doe type: object type: object PartnerTagsList: description: Partner referrer tag rows returned in the API envelope `data` field. type: array items: $ref: '#/components/schemas/PartnerTag' PartnerTermsAgreement: description: Partner T&C acceptance record returned in the API envelope `data` field. required: - partner_id - partner_agreement_version - partner_agreement_user - partner_agreement_date properties: partner_id: type: integer example: 123 partner_agreement_version: description: Accepted T&C version in wire format (e.g. "v2"). type: string example: v2 partner_agreement_user: type: integer example: 4567 partner_agreement_date: description: UTC timestamp when acceptance was recorded (ISO-8601). type: string format: date-time example: '2026-05-26T14:11:09Z' type: object PartnerTermsAgreementOk: description: Partner T&C acceptance record returned in the API envelope `data` field. type: object allOf: - $ref: '#/components/schemas/PartnerTermsAgreement' UpdateProfileAddress: description: >- When any address field is supplied, the full legacy address set is required. properties: street: type: string maxLength: 80 apartment_suite: type: string nullable: true maxLength: 80 city: type: string maxLength: 80 county: description: Ignored when country is Canada. type: string nullable: true maxLength: 80 state: type: string zip: type: string country_id: type: integer type: object UpdateProfileRequest: description: >- Partial profile update. Profile fields and address are independently optional. properties: first_name: type: string last_name: type: string company: type: string nullable: true website: type: string nullable: true phone: type: string address: oneOf: - $ref: '#/components/schemas/UpdateProfileAddress' nullable: true type: object ProfileAddress: properties: street: type: string apartment_suite: type: string nullable: true city: type: string county: description: Always empty for Canada. type: string nullable: true state: description: US 2-letter or Canadian province code. type: string zip: type: string country_id: type: integer formatted_address: type: string nullable: true lat: type: number format: float nullable: true lon: type: number format: float nullable: true type: object ProfileResponse: required: - user_id - email properties: user_id: type: integer email: type: string first_name: type: string nullable: true last_name: type: string nullable: true company: type: string nullable: true website: type: string nullable: true phone: description: Maps to profile_phone. type: string nullable: true address: oneOf: - $ref: '#/components/schemas/ProfileAddress' nullable: true type: object QualifiedReportingEntitiesListResponse: description: QRE rows returned in the API envelope `data` field. type: array items: $ref: '#/components/schemas/QualifiedReportingEntityItem' QualifiedReportingEntityItem: description: >- One QRE row in the API envelope `data` array for `getQualifiedReportingEntities`. Runtime: {@see \Qualified_reporting_entity_lib::get_qualified_reporting_entities()} — `id`, `name` (name equals id). required: - id - name properties: id: description: Qualified reporting entity identifier (`qre_id`). type: string name: description: Same as `id` in the current implementation. type: string type: object RemoteDataCollectorItem: description: >- One remote data collector row in the API envelope `data` array for `getRemoteDataCollectors`. Runtime: {@see \Reference_data_api::remote_data_collectors_get()} — `id`, `name` from autoreporters query. required: - id - name properties: id: description: Remote data collector identifier (`remote_data_collectors_id`). type: integer name: description: Display name of the remote data collector. type: string type: object RemoteDataCollectorsListResponse: description: Remote data collector rows returned in the API envelope `data` field. type: array items: $ref: '#/components/schemas/RemoteDataCollectorItem' ThirdPartyCredentialCreateRequest: required: - display_name - username - password properties: display_name: type: string username: type: string password: type: string writeOnly: true type: object ThirdPartyCredentialUpdateRequest: description: At least one of display_name, username, or password must be provided. properties: display_name: type: string nullable: true username: type: string nullable: true password: type: string writeOnly: true nullable: true type: object ThirdPartyCredential: required: - api_cred_id - display_name - username - account_id - date_created properties: api_cred_id: type: integer format: int64 display_name: type: string username: type: string account_id: type: integer format: int64 date_created: type: string format: date-time date_edited: type: string format: date-time nullable: true type: object ThirdPartyCredentialList: description: Third-party API credentials for the caller's primary account. type: array items: $ref: '#/components/schemas/ThirdPartyCredential' TransactionStatementDetailItem: required: - detail_id - vintage - product - payment_status - facility_name - net_amount properties: detail_id: type: integer example: 98765 vintage: type: string example: 03/2026 nullable: true product: type: string example: NJ Class I REC nullable: true payment_status: type: string example: Paid enum: - Paid - Payment initiated - Not yet initiated - Payment rejected - Hold - Info not available - External transaction facility_name: type: string example: SolarFarm North nullable: true net_amount: description: Net amount due to the seller after fees, in the statement currency. type: number format: float example: 142.5 type: object TransactionStatementFinancialSummary: required: - currency_labels - show_currency_labels - totals properties: currency_labels: type: array items: type: string example: - USD - CAD show_currency_labels: description: >- True when multiple currencies are present or when the only currency is not USD. type: boolean example: true totals: type: array items: $ref: '#/components/schemas/TransactionStatementFinancialSummaryTotalItem' type: object TransactionStatementFinancialSummaryTotalItem: required: - currency_label - currency_symbol - net_sales - formatted_net_sales properties: currency_label: type: string example: USD currency_symbol: type: string example: $ net_sales: type: number format: float example: 1234.56 formatted_net_sales: type: string example: $1,234.56 type: object TransactionStatementItem: required: - statement_id - statement_date - transaction_date - statement_total - currency_label - currency_symbol - details properties: statement_id: type: integer example: 2874259 statement_date: description: Statement date used for legacy dropdown ordering. type: string format: date example: '2026-03-30' transaction_date: description: Transaction date used by the legacy dropdown label. type: string format: date example: '2026-03-30' statement_total: type: number format: float example: 1234.56 currency_label: type: string example: USD currency_symbol: type: string example: $ details: type: array items: $ref: '#/components/schemas/TransactionStatementDetailItem' type: object TransactionStatementList: properties: data: type: array items: $ref: '#/components/schemas/TransactionStatementItem' type: object UserNotAvailableError: required: - error - message type: object allOf: - $ref: '#/components/schemas/Error' - properties: error: description: >- GET `/userinfo` when session user context or active account is missing. type: string example: USER_NOT_AVAILABLE message: type: string example: User information not available type: object SwitchActiveAccountRequest: description: Payload to switch the authenticated user active account. required: - account_id properties: account_id: description: Target account identifier. Must belong to the authenticated user. type: integer example: 4242 minimum: 1 type: object MeAccount: description: Single account membership for the authenticated user. required: - account_id - display_name - is_primary - is_active - role_ids properties: account_id: type: integer example: 4242 account_name: type: string example: Acme Solar LLC nullable: true display_name: type: string example: Acme Solar LLC is_primary: type: boolean example: false is_active: type: boolean example: true role_ids: type: array items: type: integer example: - 1 - 3 type: object MeAccountListResponse: description: >- Authenticated user account memberships returned in the API envelope `data` field. required: - accounts properties: accounts: type: array items: $ref: '#/components/schemas/MeAccount' type: object UserInfoResponse: description: >- Authenticated user profile, active account name, and permissions returned in the API envelope `data` field. required: - success - id - first_name - last_name - email - permissions properties: success: type: boolean example: true id: type: integer example: 123 first_name: type: string example: John last_name: type: string example: Doe email: type: string example: john.doe@example.com account_name: type: string example: Acme Solar LLC nullable: true permissions: type: array items: type: object type: object UtilitiesListResponse: description: Utility rows returned in the API envelope `data` field. type: array items: $ref: '#/components/schemas/UtilityItem' UtilityItem: description: >- One utility row in the API envelope `data` array for `getUtilitiesByCountyAndState`. Runtime: {@see \Utility_Lib::get_utilities_by_county_and_state()} — `id`, `name`. required: - id - name properties: id: description: Utilities identifier (`utilities_id`). type: integer name: description: Display name of the utility. type: string type: object ApiFieldError: description: API field error properties: field: type: string example: field_name message: type: string example: error_message type: object ApiResponseDTO: description: API response data properties: success: type: boolean example: true error: type: string example: error message message: type: string example: message data: type: object example: key: value errors: type: array items: type: string example: - error message field_errors: type: array items: $ref: '#/components/schemas/ApiFieldError' example: - field: email message: error message type: object Pagination: properties: total: description: Total number of items type: integer example: 100 per_page: description: Items per page type: integer example: 20 current_page: description: Current page number type: integer example: 1 last_page: description: Last page number type: integer example: 5 type: object AccessForbiddenError: required: - error - message type: object allOf: - $ref: '#/components/schemas/Error' - properties: error: description: >- 403 when the caller lacks permission for the requested account or resource. type: string example: access_forbidden message: type: string example: You do not have permission to access this resource type: object AuthError: required: - error - message type: object allOf: - $ref: '#/components/schemas/Error' - properties: error: description: 401 when API authentication is missing or invalid. type: string example: connection_failed message: type: string example: Authentication error type: object Error: description: >- Generic error payload when no more specific error schema applies. Implements JsonSerializable so it can be passed directly to API_Controller::response(). Implements Countable returning 0 so the response size guard in API_Controller treats it as an empty collection (error responses never trigger the size limit). required: - error - message properties: error: type: string example: error message: type: string example: Error description type: object MeterReadingOperationError: description: >- Shared `{success, error, message}` shape for meter-reading image/status API errors. Endpoint-specific schemas extend this class and set a distinct `schema` name for OpenAPI. required: - success - error - message properties: success: type: boolean example: false error: type: string example: error message: description: Human-readable error message type: string example: An error occurred type: object ServerError: required: - error - message type: object allOf: - $ref: '#/components/schemas/Error' - properties: error: description: 500 when an unexpected server or infrastructure failure occurs. type: string example: internal_error message: type: string example: Error description type: object ValidationError: description: >- Error payload for validation failures (400, 422) on request bodies and business rules. For query/path validation, see {@see \Trade\CodeIgniter\dtos\shared\responses\errors\QueryValidationErrorResponse} (`details` keyed by parameter name; this class uses `data` for the same purpose). properties: error: type: string example: validation_error message: type: string example: Errors when validating data data: type: object additionalProperties: true type: object QueryValidationErrorResponse: description: >- Generic 400 payload for query/path validation errors. Intended for read/query failures: - simple bad requests (`error` only, optional `message`) - field-level details via `details` (same role as `data` on {@see \Trade\CodeIgniter\dtos\shared\errors\ValidationError}) properties: error: description: Error code or error message. type: string example: invalid_parameters message: description: Human-friendly explanation of the bad request. type: string example: >- One or more query parameters are invalid. Check details for field-specific messages. nullable: true details: description: Structured details keyed by query parameter/path. type: object example: status: 'Invalid value ''pending''. Allowed: active, inactive' nullable: true additionalProperties: true type: object MobileAppConfigResponse: description: Configuration used by the mobile app at startup. required: - features - user - limits - ocr properties: features: required: - face_id - camera_scanning - manual_entry - image_upload properties: face_id: type: boolean example: true camera_scanning: type: boolean example: true manual_entry: type: boolean example: true image_upload: type: boolean example: true type: object user: $ref: '#/components/schemas/MobileAppEligibilityResult' limits: required: - max_image_size - max_image_dimension properties: max_image_size: description: >- Maximum image upload size in bytes. Environment overrides may be returned as strings. example: 10485760 oneOf: - type: integer - type: string max_image_dimension: description: >- Maximum image dimension in pixels. Environment overrides may be returned as strings. example: 4096 oneOf: - type: integer - type: string type: object ocr: required: - supported_formats properties: supported_formats: type: array items: type: string enum: - JPEG - PNG - HEIC type: object type: object MobileAppEligibilityResult: description: Eligibility details for the authenticated user to use the mobile app. required: - can_use_app - code - message - facility_id - meter_id properties: can_use_app: type: boolean example: true code: description: Eligibility error code when the user cannot use the mobile app. type: string example: NO_FACILITY nullable: true message: type: string example: >- No facility found. Please contact support if you believe this is an error. nullable: true facility_id: type: integer example: 123 nullable: true meter_id: type: integer example: 456 nullable: true type: object ApplyFixRequestBody: required: - error_type - field properties: error_type: type: string field: type: string fix_value: type: string nullable: true error_index: type: integer nullable: true type: object ForbiddenResponse: properties: success: type: boolean enum: - false error: type: string example: You do not have permission to access this upload. type: object ApplyFixFieldErrorResponse: properties: success: type: boolean enum: - false error: type: string enum: - The error_type field is required. - The field field is required. - Invalid error_index value. type: object ValidationErrorsMap: description: Map of row identifier to validation error detail. type: object additionalProperties: true ApplyFixValidationErrorResponse: required: - success - error - validation - facility_creation_attempt properties: success: type: boolean enum: - false error: type: string validation: $ref: '#/components/schemas/ValidationErrorsMap' facility_creation_attempt: type: array items: type: object type: object SingleFixSuccessResponse: required: - success - facility_creation_attempt properties: success: type: boolean enum: - true facility_creation_attempt: type: object additionalProperties: true type: object BulkFixSuccessResponse: required: - success - facility_creation_attempt - validation properties: success: type: boolean enum: - true facility_creation_attempt: type: object additionalProperties: true validation: description: May be an empty array [] or a ValidationErrorsMap object. oneOf: - type: array items: type: object - $ref: '#/components/schemas/ValidationErrorsMap' type: object UserListItem: required: - id - first_name - last_name - name properties: id: description: User identifier (`users.id`). type: integer first_name: description: First name from `user_profiles`. type: string last_name: description: Last name from `user_profiles`. type: string name: description: Display name (`first_name` + `last_name`). type: string type: object UsersListResponse: description: User rows returned in the API envelope `data` field. type: array items: $ref: '#/components/schemas/UserListItem' PaginatedResponseEnvelope: description: Standard API response envelope for paginated endpoints. type: object allOf: - $ref: '#/components/schemas/ResponseEnvelope' - required: - pagination properties: pagination: $ref: '#/components/schemas/Pagination' type: object ResponseEnvelope: description: >- Standard API response envelope. Every response wraps its payload in this structure; the `data` field contains the operation-specific payload. required: - url - date - code - elements - page properties: url: description: Request URL including query string. type: string example: /app/api/v1/facilities date: description: Response timestamp. type: string example: 2024-09-17 04:26:39 EDT code: description: >- HTTP status code, mirrored in the JSON body (same as the response status). type: integer elements: description: Number of items in `data` on HTTP 200; 0 for other status codes. type: integer page: description: >- Page label (`1 of 1` when not paginated) or numeric page when listing with pagination. oneOf: - type: string example: 1 of 1 - type: integer example: 2 type: object parameters: FacilityListQueryProgram: name: program in: query description: Narrows the list to facilities in a program. required: false schema: type: string example: dgg enum: - dgg FacilityListQuerySearch: name: search in: query description: Search term matched against facility name required: false schema: type: string example: solar FacilityListQueryUtilityIds: name: utility_ids in: query description: Comma-separated utility IDs to filter by required: false schema: type: string example: 12,34 FacilityListQueryAutoReporterIds: name: auto_reporter_ids in: query description: Comma-separated auto-reporter (remote data collector) IDs to filter by required: false schema: type: string example: 5,6 FacilityListQueryCommencementDateFrom: name: commencement_date_from in: query description: Filter by facility commencement date on or after this day (YYYY-MM-DD) required: false schema: type: string format: date example: '2024-01-01' FacilityListQueryCommencementDateTo: name: commencement_date_to in: query description: Filter by facility commencement date on or before this day (YYYY-MM-DD) required: false schema: type: string format: date example: '2025-12-31' FacilityListQueryExcludeManagedFacilityGroupMembers: name: exclude_managed_facility_group_members in: query description: >- When true, omit facilities that belong to a managed facility group owned by the listed account. required: false schema: type: boolean example: true FacilityListQueryTicketOwnerIds: name: ticket_owner_ids in: query description: Comma-separated ticket owner user IDs to filter by required: false schema: type: string example: 125,126 MfgGroupListQueryPage: name: page in: query required: false schema: type: integer default: 1 minimum: 1 MfgGroupListQueryPerPage: name: per_page in: query description: >- Number of managed facility groups to return per page (capped by server config, typically max 1000). required: false schema: type: integer default: 50 minimum: 1 MfgGroupListQuerySortBy: name: sort_by in: query description: >- Repeatable sort tokens `field:direction` (e.g. `name:ASC`). Fields: `name`, `group_real_capacity_kw`, `ticket_owner`, `commencement_date`, `created_at`. Invalid fields return 400. required: false style: form explode: true schema: type: array items: type: string MfgGroupListQuerySearch: name: search in: query description: Search by managed facility group name. required: false schema: type: string MfgGroupListQueryAccountId: name: account_id in: query description: >- Account ID to query. Restricted to admin users; non-admin requests with this parameter return 403. required: false schema: type: integer MfgGroupListQueryStatus: name: status in: query description: >- Membership status filter. Allowed values match `ManagedFacilityGroupsMembershipStatus`, e.g. `admin_review`, `approved`, `complete`, `minted`, `submitted`. required: false schema: type: string enum: - admin_review - approved - complete - minted - submitted MfgGroupListQueryCapacity: name: capacity in: query description: >- Capacity bucket filter. Allowed: `below_250`, `below_360`, `above_250`, `above_360`. required: false schema: type: string enum: - below_250 - below_360 - above_250 - above_360 MfgGroupListQueryUtilityIds: name: utility_ids in: query description: Comma-separated utility IDs to filter by required: false schema: type: string example: 12,34 MfgGroupListQueryAutoReporterIds: name: auto_reporter_ids in: query description: Comma-separated auto-reporter (remote data collector) IDs to filter by required: false schema: type: string example: 5,6 MfgGroupListQueryCommencementDateFrom: name: commencement_date_from in: query description: Filter groups with facility commencement date on or after this date. required: false schema: type: string format: date MfgGroupListQueryCommencementDateTo: name: commencement_date_to in: query description: Filter groups with facility commencement date on or before this date. required: false schema: type: string format: date MfgGroupListQueryTicketOwnerIds: name: ticket_owner_ids in: query description: Comma-separated ticket owner user IDs to filter by required: false schema: type: string example: 125,126 MfgWorkflowQueryAccountId: name: account_id in: query description: >- Owner account for the groups. Defaults to the active account. **Admin only**; non-admins receive 403 if set. required: false schema: type: integer example: 4242 minimum: 1 PartnerListQuerySearch: name: search in: query description: Search term required: false schema: type: string example: solar PartnerListQueryStatus: name: status in: query description: Filter by partner status. required: false schema: type: string example: active enum: - active - inactive PartnerListQueryOwnership: name: ownership in: query description: Filter by ownership preference. required: false schema: type: string example: 'yes' enum: - 'yes' - 'no' PartnerListQueryDateFrom: name: date_from in: query description: Filter by partner creation date on or after this day (YYYY-MM-DD). required: false schema: type: string format: date example: '2025-01-01' PartnerListQueryDateTo: name: date_to in: query description: Filter by partner creation date on or before this day (YYYY-MM-DD). required: false schema: type: string format: date example: '2025-12-31' TransactionStatementFilterSearch: name: search in: query description: >- Search term (matches statement_date, transaction_date, or statement_total). required: false schema: type: string example: '2025-01-01' TransactionStatementFilterDateFrom: name: date_from in: query description: Filter statements on or after this date (YYYY-MM-DD). required: false schema: type: string format: date example: '2025-01-01' TransactionStatementFilterDateTo: name: date_to in: query description: Filter statements on or before this date (YYYY-MM-DD). required: false schema: type: string format: date example: '2025-12-31' PaginatedSortedSearchQueryPage: name: page in: query description: 'Page number for pagination (default: 1)' required: false schema: type: integer example: 1 minimum: 1 PaginatedSortedSearchQueryPerPage: name: per_page in: query description: Number of items per page required: false schema: type: integer example: 20 minimum: 1 PaginatedSortedSearchQuerySortBy: name: sort_by[] in: query description: >- Sort keys as repeated query parameters. Each entry is `field` or `field:direction` (direction: ASC or DESC). Example: `sort_by[]=name:ASC&sort_by[]=id:DESC` required: false schema: type: array items: type: string example: - referrer_name:ASC securitySchemes: BearerAuth: type: http scheme: bearer security: - BearerAuth: [] tags: - name: Facilities description: Facilities - name: Transactions description: Transactions - name: Facility import error handling description: Facility import error handling - name: Meter Readings description: Meter Readings - name: Clean Transportation description: Clean Transportation - name: Market description: Market - name: Qualified reporting entities description: Qualified reporting entities - name: Building types description: Building types - name: Remote data collectors description: Remote data collectors - name: Utilities description: Utilities