{
 "$schema": "https://json-schema.org/draft/2020-12/schema",
 "$id": "urn:luminetric:canonical:1:statement.available",
 "$comment": "Generated by build_schemas.py. Open by design: accept fields and enum values you do not recognise (COMPATIBILITY.md).",
 "title": "statement.available",
 "description": "One statement. Sent with Luminetric-Event: statement.available.",
 "type": "object",
 "properties": {
  "schema_version": {
   "type": "string",
   "pattern": "^1\\.[0-9]+\\.[0-9]+$",
   "description": "Semantic version of this contract. This schema covers major version 1."
  },
  "statement_id": {
   "type": "string",
   "pattern": "^stt_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
   "description": "Durable across re-extractions. Key what you store on it."
  },
  "version_id": {
   "type": "string",
   "pattern": "^ver_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
   "description": "This extraction of the statement. The delivery id."
  },
  "is_latest": {
   "type": "boolean",
   "description": "True when sent. Choose between two versions you hold by extracted_at, not by this flag."
  },
  "superseded_by": {
   "type": [
    "string",
    "null"
   ],
   "pattern": "^ver_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
   "description": "Reserved; null when sent. A later version does not update a version you already hold."
  },
  "extracted_at": {
   "type": "string",
   "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?Z$",
   "description": "When this version was extracted. Fractional-second digits vary (none, 3, 6 or 9): compare as a time, not as a string. Of two versions of a statement, the later extracted_at is current. ISO 8601, UTC."
  },
  "request_id": {
   "type": "string",
   "minLength": 1,
   "description": "The request the bill arrived in."
  },
  "client_reference": {
   "type": "string",
   "minLength": 1,
   "maxLength": 255,
   "description": "Echoed from the request when it carried one."
  },
  "connection_id": {
   "type": "string",
   "pattern": "^con_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
   "description": "The connection the bill was retrieved through. Present only on a retrieved bill; absent on an uploaded one. Do not require it."
  },
  "account_id": {
   "type": "string",
   "pattern": "^acc_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
   "description": "The account the bill was retrieved for: match on this, not on the account number, which the bill may print in another form. Present when a retrieved bill matched one of its connection's accounts; absent otherwise and on uploaded bills. Do not require it."
  },
  "document_id": {
   "type": "string",
   "pattern": "^doc_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
   "description": "The received file; shared by every statement in it."
  },
  "parent_statement_id": {
   "type": "string",
   "pattern": "^stt_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
   "description": "Present only on a child of a summary bill."
  },
  "provider": {
   "$ref": "#/$defs/provider"
  },
  "issuer": {
   "type": [
    "object",
    "null"
   ],
   "description": "Reserved. Always null today."
  },
  "statement": {
   "$ref": "#/$defs/statement_body"
  },
  "children": {
   "$ref": "#/$defs/children"
  },
  "reconciliation": {
   "$ref": "#/$defs/reconciliation"
  },
  "confidence": {
   "$ref": "#/$defs/confidence"
  },
  "meters": {
   "type": "array",
   "items": {
    "$ref": "#/$defs/meter"
   }
  },
  "sections": {
   "type": "array",
   "items": {
    "$ref": "#/$defs/section"
   }
  },
  "charges": {
   "type": "array",
   "items": {
    "$ref": "#/$defs/charge"
   }
  }
 },
 "required": [
  "schema_version",
  "statement_id",
  "version_id",
  "is_latest",
  "superseded_by",
  "extracted_at",
  "request_id",
  "issuer",
  "statement",
  "reconciliation",
  "confidence",
  "meters",
  "sections",
  "charges"
 ],
 "$defs": {
  "service": {
   "type": "string",
   "description": "The utility service. 'unknown' means the charge belongs to one of the bill's services but the bill does not say which; 'none' means it belongs to no service at all (a bill-wide tax, a payment, a late fee). A tax or fee you cannot attribute is 'none', never 'unknown'. Attribute a tax or fee to its service whenever the bill allows - a sales tax inside an electric section, or on an electric-only bill, is 'electric'. Never guess, and never fall back to the bill's dominant service. Known values: electric, gas, water, sewer, irrigation, stormwater, drainage, trash, fire, steam, chilled_water, internet, phone, tv, none, other, unknown. Values may be added in a minor version. Treat an unknown value as 'other'.",
   "x-known-values": [
    "electric",
    "gas",
    "water",
    "sewer",
    "irrigation",
    "stormwater",
    "drainage",
    "trash",
    "fire",
    "steam",
    "chilled_water",
    "internet",
    "phone",
    "tv",
    "none",
    "other",
    "unknown"
   ]
  },
  "money": {
   "type": "object",
   "description": "A currency figure. 'amount' is signed: credits and payments are negative.",
   "properties": {
    "amount": {
     "type": "number"
    },
    "as_printed": {
     "type": "string",
     "description": "Exactly as it appears on the bill, including $, commas, CR or parentheses."
    },
    "source": {
     "$ref": "#/$defs/source"
    }
   },
   "required": [
    "amount",
    "as_printed",
    "source"
   ]
  },
  "quantity": {
   "type": "object",
   "description": "A measured quantity with its unit kept separate from its value.",
   "properties": {
    "value": {
     "type": "number"
    },
    "unit": {
     "type": "string",
     "description": "As printed, e.g. 'kWh', 'KGAL', 'therms', 'gallons'."
    },
    "as_printed": {
     "type": "string",
     "description": "Exactly as it appears on the bill, e.g. '139 KGAL'."
    },
    "source": {
     "$ref": "#/$defs/source"
    }
   },
   "required": [
    "value",
    "unit",
    "as_printed",
    "source"
   ]
  },
  "source": {
   "type": "string",
   "description": "Where the value came from. 'extracted' = read off the bill. 'derived' = computed from other values on the bill. 'defaulted' = supplied because the bill omitted it. 'inferred' = judged from context. Never mark a computed value 'extracted'. Known values: extracted, derived, defaulted, inferred. Values may be added in a minor version.",
   "x-known-values": [
    "extracted",
    "derived",
    "defaulted",
    "inferred"
   ]
  },
  "dated": {
   "type": "object",
   "description": "A date. 'value' is ISO 8601 (YYYY-MM-DD). 'as_printed' is the date exactly as the bill shows it. 'source' says whether a labelled field supplied it - a date taken from an unlabelled position is NOT 'extracted'.",
   "properties": {
    "value": {
     "type": "string",
     "description": "YYYY-MM-DD."
    },
    "as_printed": {
     "type": "string",
     "description": "Exactly as printed: '08/07/26', 'Aug 7, 2026'."
    },
    "source": {
     "$ref": "#/$defs/source"
    }
   },
   "required": [
    "value",
    "as_printed",
    "source"
   ]
  },
  "identifier": {
   "type": "object",
   "description": "An account, meter or premise identifier. 'value' is the identifier alone, with no label, read type or composite suffix. 'source' says whether a labelled field supplied it.",
   "properties": {
    "value": {
     "type": "string",
     "description": "The identifier alone. Never a word such as ACTUAL or a column heading."
    },
    "as_printed": {
     "type": "string",
     "description": "The identifier as printed, including any composite form."
    },
    "source": {
     "$ref": "#/$defs/source"
    },
    "normalized": {
     "type": "string",
     "description": "The identifier's letters and digits only, upper case, derived by Luminetric from 'value'. The same printed number always gives the same key however it was punctuated, so key accounts, locations and meters on this rather than on 'value' or 'as_printed'."
    }
   },
   "required": [
    "value",
    "as_printed",
    "source"
   ]
  },
  "statement_body": {
   "type": "object",
   "properties": {
    "service_address": {
     "type": "string"
    },
    "statement_date": {
     "$ref": "#/$defs/dated"
    },
    "due_date": {
     "$ref": "#/$defs/dated"
    },
    "period_start": {
     "$ref": "#/$defs/dated"
    },
    "period_end": {
     "$ref": "#/$defs/dated"
    },
    "currency": {
     "type": "string",
     "description": "ISO 4217, normally USD"
    },
    "is_summary": {
     "type": "boolean",
     "description": "true when the bill aggregates several accounts or premises"
    },
    "totals": {
     "type": "object",
     "properties": {
      "amount_due": {
       "$ref": "#/$defs/money"
      },
      "current_charges": {
       "$ref": "#/$defs/money",
       "description": "Charges for this period alone, excluding any prior balance. Where the bill prints no separate figure, compute it and mark source 'derived' - never omit it and never pass a computed figure as 'extracted'."
      },
      "previous_balance": {
       "$ref": "#/$defs/money"
      },
      "balance_forward": {
       "$ref": "#/$defs/money"
      },
      "late_fee": {
       "$ref": "#/$defs/money"
      },
      "payments": {
       "$ref": "#/$defs/money"
      },
      "credits": {
       "$ref": "#/$defs/money"
      },
      "adjustments": {
       "$ref": "#/$defs/money"
      }
     },
     "required": [
      "amount_due",
      "current_charges"
     ]
    },
    "account_number": {
     "$ref": "#/$defs/identifier"
    },
    "location_id": {
     "$ref": "#/$defs/identifier"
    },
    "children": {
     "type": "object",
     "description": "Present ONLY on a parent. How many child statements this bill says it has, and where that count came from. A consumer checks the delivered children against it, so a truncated extraction is detectable instead of silent.",
     "properties": {
      "expected_count": {
       "type": "integer",
       "description": "The number of children the bill itself states or enumerates."
      },
      "source": {
       "$ref": "#/$defs/source"
      }
     },
     "required": [
      "expected_count",
      "source"
     ]
    }
   },
   "required": [
    "statement_date",
    "totals"
   ],
   "description": "The statement itself, as extracted. The ids that place it - statement_id, parent_statement_id - are on the envelope, not here."
  },
  "meter": {
   "type": "object",
   "properties": {
    "meter_id": {
     "type": "string",
     "description": "Local id used to reference this meter from sections and charges."
    },
    "service": {
     "$ref": "#/$defs/service"
    },
    "service_address": {
     "type": "string"
    },
    "previous_read": {
     "type": "string"
    },
    "current_read": {
     "type": "string"
    },
    "multiplier": {
     "type": "string"
    },
    "read_start": {
     "$ref": "#/$defs/dated"
    },
    "read_end": {
     "$ref": "#/$defs/dated"
    },
    "usage": {
     "$ref": "#/$defs/quantity"
    },
    "number": {
     "$ref": "#/$defs/identifier"
    }
   },
   "required": [
    "meter_id",
    "service"
   ],
   "description": "One entry per metered point of service. Empty when the bill shows no meter."
  },
  "section": {
   "type": "object",
   "properties": {
    "section_id": {
     "type": "string"
    },
    "heading_as_printed": {
     "type": "string",
     "description": "The heading text exactly as printed, e.g. 'CURRENT CHARGES - ELECTRIC'. Empty string when the grouping had no printed heading."
    },
    "service": {
     "$ref": "#/$defs/service"
    },
    "service_source": {
     "type": "string",
     "description": "How the section's service was determined. Known values: heading, summary_row, item_text, meter, single_service_bill, inferred, none. Values may be added in a minor version.",
     "x-known-values": [
      "heading",
      "summary_row",
      "item_text",
      "meter",
      "single_service_bill",
      "inferred",
      "none"
     ]
    },
    "meter_id": {
     "type": "string",
     "description": "Meter this section belongs to, when the bill ties it to one."
    },
    "ordinal": {
     "type": "integer"
    },
    "printed_total": {
     "$ref": "#/$defs/money"
    }
   },
   "required": [
    "section_id",
    "heading_as_printed",
    "service",
    "service_source",
    "ordinal"
   ],
   "description": "The printed groupings of charges on the bill, in page order. THIS IS THE FIELD THAT CARRIES SERVICE ATTRIBUTION. Create one section per printed grouping even when the grouping is only a labelled row in a summary table."
  },
  "charge": {
   "type": "object",
   "properties": {
    "name_as_printed": {
     "type": "string",
     "description": "The charge description exactly as printed. Never normalised."
    },
    "section_id": {
     "type": "string",
     "description": "The section this row sat under. Required; use the catch-all section when the bill has no grouping."
    },
    "meter_id": {
     "type": "string",
     "description": "Omit when the charge is account-level and cannot be attributed to a meter."
    },
    "service": {
     "$ref": "#/$defs/service"
    },
    "kind": {
     "type": "string",
     "x-known-values": [
      "supply",
      "delivery",
      "tax",
      "fee",
      "credit",
      "adjustment",
      "subtotal",
      "other",
      "unknown"
     ],
     "description": "Known values: supply, delivery, tax, fee, credit, adjustment, subtotal, other, unknown. Values may be added in a minor version. Treat an unknown value as 'other'."
    },
    "contributes_to_total": {
     "type": "boolean",
     "description": "false for printed subtotals and for any row already counted inside another row, so a consumer can sum without double-counting."
    },
    "amount": {
     "$ref": "#/$defs/money"
    },
    "quantity": {
     "$ref": "#/$defs/quantity"
    },
    "rate": {
     "type": "string",
     "description": "Unit rate exactly as printed."
    },
    "period_start": {
     "$ref": "#/$defs/dated"
    },
    "period_end": {
     "$ref": "#/$defs/dated"
    },
    "service_source": {
     "type": "string",
     "description": "How this charge's service was decided. 'section' = inherited from its section, the normal case. 'item_text' = the charge's own printed words name the service. 'single_service_bill' = the bill carries exactly one service and the charge took it. 'convention' = a house rule decided it, e.g. a line naming two services takes the first. 'none' = the service is 'none' or 'unknown', so nothing decided it. Known values: section, item_text, meter, summary_row, single_service_bill, convention, inferred, none. Values may be added in a minor version.",
     "x-known-values": [
      "section",
      "item_text",
      "meter",
      "summary_row",
      "single_service_bill",
      "convention",
      "inferred",
      "none"
     ]
    }
   },
   "required": [
    "name_as_printed",
    "section_id",
    "service",
    "kind",
    "contributes_to_total",
    "amount",
    "service_source"
   ],
   "description": "Every charge row on the bill, including printed subtotals (marked kind=subtotal)."
  },
  "provider": {
   "type": "object",
   "description": "NOT YET SENT. Reserved for the provider resolved from the bill; it will arrive in a minor version. Do not require it.",
   "properties": {
    "provider_id": {
     "type": "string",
     "pattern": "^prv_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
     "description": "Luminetric provider id."
    },
    "slug": {
     "type": "string"
    },
    "display_name": {
     "type": "string"
    },
    "resolved_from": {
     "type": "string"
    },
    "asserted_by_client": {
     "type": [
      "string",
      "null"
     ]
    },
    "assertion_agrees": {
     "type": [
      "boolean",
      "null"
     ]
    }
   }
  },
  "children": {
   "type": "object",
   "description": "Present only on a parent. The children this bill has, declared as data.",
   "properties": {
    "expected_count": {
     "type": [
      "integer",
      "null"
     ],
     "minimum": 0,
     "description": "How many children the bill states. Compare with the number delivered."
    },
    "source": {
     "type": [
      "string",
      "null"
     ],
     "description": "Where expected_count came from. Known values: extracted, derived, defaulted, inferred. Values may be added in a minor version.",
     "x-known-values": [
      "extracted",
      "derived",
      "defaulted",
      "inferred"
     ]
    },
    "delivered": {
     "type": "array",
     "items": {
      "type": "object",
      "properties": {
       "statement_id": {
        "type": "string",
        "pattern": "^stt_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
        "description": "The child statement."
       },
       "account_number": {
        "type": [
         "string",
         "null"
        ],
        "description": "The child's account number as printed (its value, not normalized)."
       },
       "service_address": {
        "type": [
         "string",
         "null"
        ]
       },
       "meter_numbers": {
        "type": "array",
        "items": {
         "type": "string"
        },
        "description": "Empty for an unmetered child. Match children by meter, not by account or address."
       },
       "current_charges": {
        "type": [
         "number",
         "null"
        ]
       }
      },
      "required": [
       "statement_id",
       "account_number",
       "service_address",
       "meter_numbers",
       "current_charges"
      ]
     }
    }
   },
   "required": [
    "expected_count",
    "source",
    "delivered"
   ]
  },
  "reconciliation": {
   "type": "object",
   "description": "Recomputed from this payload. Reported, never a gate.",
   "properties": {
    "identity": {
     "type": "string",
     "description": "The formula checked."
    },
    "current_charges": {
     "type": [
      "number",
      "null"
     ]
    },
    "sum_of_charges": {
     "type": "number"
    },
    "sum_of_parent_charges": {
     "type": "number",
     "description": "Parents only."
    },
    "sum_of_child_current_charges": {
     "type": "number",
     "description": "Parents only."
    },
    "children_include_adjustments": {
     "type": "number",
     "description": "Parents only, and only when the children balance with the bill's adjustments rather than its current charges."
    },
    "delta": {
     "type": [
      "number",
      "null"
     ]
    },
    "balanced": {
     "type": "boolean"
    },
    "total_due": {
     "type": "number"
    },
    "total_due_identity": {
     "type": "string"
    },
    "total_due_delta": {
     "type": "number"
    },
    "total_due_balanced": {
     "type": "boolean"
    }
   },
   "required": [
    "identity",
    "current_charges",
    "sum_of_charges",
    "delta",
    "balanced"
   ]
  },
  "confidence": {
   "type": "object",
   "properties": {
    "tier": {
     "type": "string",
     "description": "deliver when every signal holds, review when any fails. Known values: deliver, review. Values may be added in a minor version. Treat an unknown value as 'review'.",
     "x-known-values": [
      "deliver",
      "review"
     ]
    },
    "reconciles": {
     "type": "boolean"
    },
    "key_totals_extracted": {
     "type": "boolean"
    },
    "unknown_service_charge_count": {
     "type": "integer",
     "minimum": 0
    },
    "signals_failed": {
     "type": "array",
     "items": {
      "type": "string",
      "description": "A signal that did not hold. Known values: reconciliation, unknown_service_charges, key_totals_not_extracted. Values may be added in a minor version. Treat an unknown entry as a failed signal.",
      "x-known-values": [
       "reconciliation",
       "unknown_service_charges",
       "key_totals_not_extracted"
      ]
     }
    }
   },
   "required": [
    "tier",
    "reconciles",
    "key_totals_extracted",
    "unknown_service_charge_count",
    "signals_failed"
   ]
  }
 }
}
