openapi: 3.1.0
info:
  title: SlimHuys API
  version: '0.1.0'
  description: |
    Publieke API voor dynamische stroom- en gasprijzen in Nederland.
    Bron-data: ENTSO-E Transparency Platform (day-ahead, NL bidding zone).

    Alle prijzen zijn in EUR. ENTSO-E levert markt-prijzen in EUR/MWh;
    we exposen ook EUR/kWh en cent/kWh ter gemak. Met `?supplier=<slug>`
    retourneren we ook all-in EUR/kWh inclusief energiebelasting,
    leverancier-opslag en BTW.
  contact:
    url: https://slimhuys.nl
servers:
  - url: https://api.slimhuys.nl
    description: Productie
tags:
  - name: prices
    description: Markt- en all-in elektriciteitsprijzen
  - name: suppliers
    description: Energieleveranciers in NL met hun opslag-tarieven
  - name: meta
    description: Service-metadata (health, OpenAPI-spec)
paths:
  /health:
    get:
      tags: [meta]
      summary: Liveness + database-check
      responses:
        '200':
          description: Service en database zijn beschikbaar
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Health'
        '503':
          description: Database niet beschikbaar
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Health'
  /v1/prices/electricity/now:
    get:
      tags: [prices]
      summary: Huidige prijs (lopend kwartier)
      parameters:
        - $ref: '#/components/parameters/Supplier'
      responses:
        '200':
          description: Prijs voor het lopende kwartier
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PricePoint'
        '404':
          description: Onbekende supplier-slug
        '503':
          description: Geen prijs- of tax-data beschikbaar
  /v1/prices/electricity/today:
    get:
      tags: [prices]
      summary: Alle prijzen voor vandaag (NL-kalenderdag)
      parameters:
        - $ref: '#/components/parameters/Supplier'
      responses:
        '200':
          description: 96 kwartier-prijzen (15-min resolutie)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PriceDay'
  /v1/prices/electricity/tomorrow:
    get:
      tags: [prices]
      summary: Alle prijzen voor morgen
      description: |
        Vanaf ~14:00 NL beschikbaar nadat ENTSO-E het day-ahead-resultaat
        publiceert. Vóór die tijd is `count: 0`.
      parameters:
        - $ref: '#/components/parameters/Supplier'
      responses:
        '200':
          description: Kwartier-prijzen voor morgen, of leeg
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PriceDay'
  /v1/prices/electricity/range:
    get:
      tags: [prices]
      summary: Prijzen voor een specifieke periode
      parameters:
        - in: query
          name: from
          required: true
          schema:
            type: string
            format: date-time
          description: Inclusief begin (ISO-8601 UTC)
        - in: query
          name: to
          required: true
          schema:
            type: string
            format: date-time
          description: Exclusief eind (ISO-8601 UTC). Max 31 dagen vanaf `from`.
        - $ref: '#/components/parameters/Supplier'
      responses:
        '200':
          description: Prijzen binnen `[from, to)`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PriceRange'
        '400':
          description: Ontbrekende of ongeldige parameter
  /v1/prices/electricity/stats:
    get:
      tags: [prices]
      summary: Statistieken over een periode
      description: |
        Min, max, gemiddeld, mediaan, P25 en P75 van de markt-prijzen
        binnen `[from, to)`. Default-periode is de laatste 7 dagen.
        Handig voor relatieve drempels (bv. status-in-kleur).
      parameters:
        - in: query
          name: from
          schema:
            type: string
            format: date-time
          description: Optioneel; default = nu - 7 dagen
        - in: query
          name: to
          schema:
            type: string
            format: date-time
          description: Optioneel; default = nu
      responses:
        '200':
          description: Statistieken
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PriceStats'
        '404':
          description: Geen data in de periode
  /v1/prices/electricity/cheapest-window:
    get:
      tags: [prices]
      summary: Goedkoopste aaneengesloten venster
      parameters:
        - in: query
          name: duration_minutes
          schema:
            type: integer
            minimum: 15
            maximum: 1440
            default: 60
          description: Vensterlengte in minuten
        - $ref: '#/components/parameters/Supplier'
      responses:
        '200':
          description: Begin en eind van het goedkoopste venster
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheapestWindow'
        '404':
          description: Onvoldoende prijsdata
  /v1/suppliers:
    get:
      tags: [suppliers]
      summary: Actieve leveranciers met hun opslagen
      responses:
        '200':
          description: Lijst leveranciers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SupplierList'
components:
  parameters:
    Supplier:
      in: query
      name: supplier
      schema:
        type: string
      description: Optionele leverancier-slug; activeert all-in prijs in respons.
  schemas:
    Health:
      type: object
      required: [status, service, ts, checks]
      properties:
        status:
          type: string
          enum: [ok, degraded]
        service:
          type: string
        ts:
          type: string
          format: date-time
        checks:
          type: object
          properties:
            database:
              type: string
              enum: [ok, fail]
    Market:
      type: object
      required: [eur_per_mwh, eur_per_kwh, cents_per_kwh]
      properties:
        eur_per_mwh:
          type: number
        eur_per_kwh:
          type: number
        cents_per_kwh:
          type: number
    AllIn:
      type: object
      required: [supplier_slug, supplier_name, eur_per_kwh, cents_per_kwh]
      properties:
        supplier_slug:
          type: string
        supplier_name:
          type: string
        supplier_markup_eur_per_kwh:
          type: number
        energy_tax_eur_per_kwh:
          type: number
        subtotal_eur_per_kwh:
          type: number
        vat_rate:
          type: number
        vat_eur_per_kwh:
          type: number
        eur_per_kwh:
          type: number
        cents_per_kwh:
          type: number
    PricePoint:
      type: object
      required: [ts, ts_local, resolution_minutes, bidding_zone, market]
      properties:
        ts:
          type: string
          format: date-time
        ts_local:
          type: string
          format: date-time
        resolution_minutes:
          type: integer
          enum: [15, 30, 60]
        bidding_zone:
          type: string
        market:
          $ref: '#/components/schemas/Market'
        all_in:
          $ref: '#/components/schemas/AllIn'
    PriceDay:
      type: object
      required: [date, bidding_zone, count, points]
      properties:
        date:
          type: string
          format: date
        bidding_zone:
          type: string
        count:
          type: integer
        points:
          type: array
          items:
            $ref: '#/components/schemas/PricePoint'
    PriceRange:
      type: object
      required: [from, to, bidding_zone, count, points]
      properties:
        from:
          type: string
          format: date-time
        to:
          type: string
          format: date-time
        bidding_zone:
          type: string
        count:
          type: integer
        points:
          type: array
          items:
            $ref: '#/components/schemas/PricePoint'
    CheapestWindow:
      type: object
      required: [duration_minutes, from, to, average_market_eur_per_mwh]
      properties:
        duration_minutes:
          type: integer
        from:
          type: string
          format: date-time
        to:
          type: string
          format: date-time
        from_local:
          type: string
          format: date-time
        to_local:
          type: string
          format: date-time
        average_market_eur_per_mwh:
          type: number
        average_market_eur_per_kwh:
          type: number
        average_all_in_eur_per_kwh:
          type: number
        average_all_in_cents_per_kwh:
          type: number
        supplier_slug:
          type: string
    PriceStats:
      type: object
      required: [from, to, bidding_zone, count, min_eur_per_mwh, max_eur_per_mwh, avg_eur_per_mwh, median_eur_per_mwh, p25_eur_per_mwh, p75_eur_per_mwh]
      properties:
        from:
          type: string
          format: date-time
        to:
          type: string
          format: date-time
        bidding_zone:
          type: string
        count:
          type: integer
        min_eur_per_mwh:
          type: number
        max_eur_per_mwh:
          type: number
        avg_eur_per_mwh:
          type: number
        median_eur_per_mwh:
          type: number
        p25_eur_per_mwh:
          type: number
        p75_eur_per_mwh:
          type: number
    SupplierList:
      type: object
      required: [count, suppliers]
      properties:
        count:
          type: integer
        suppliers:
          type: array
          items:
            type: object
            required: [slug, name, markup_eur_per_kwh]
            properties:
              slug:
                type: string
              name:
                type: string
              markup_eur_per_kwh:
                type: number
              monthly_fee_eur:
                type: number
                nullable: true
