openapi: 3.0.3
info:
  title: vorsorgefonds.ch Vorsorge-API
  version: 0.1.0-beta
  description: |
    Schweizer Säule-3a- und Freizügigkeitsdaten von vorsorgefonds.ch:
    Vorsorgefonds (Bankfonds), 3a-App-Strategien (VIAC, frankly, finpension & Co.)
    und Kontozinsen – dieselben Daten wie auf der Website, laufend gepflegt.

    **Beta / Early Access:** Zugang mit persönlichem Key
    (`Authorization: Bearer <key>`), Registrierung auf
    https://vorsorgefonds.ch/api. Endpoints und Antwortformate können sich
    in der Beta noch ändern.

    **Kosten-Semantik:** Bei Fonds sind `kostenProJahrProzent` effektive
    Jahreskosten inkl. TER; bei App-Strategien sind `allInGebuehrenProzent`
    All-in-Gebühren (Verwaltung + Produktkosten). Die beiden sind nicht 1:1
    vergleichbar. Renditen 3J/5J sind annualisiert (p.a.).

    Keine Anlageberatung. Historische Renditen sind keine Garantie für
    zukünftige Entwicklungen. Alle Angaben ohne Gewähr.
  contact:
    email: kontakt@vorsorgefonds.ch
    url: https://vorsorgefonds.ch/api
servers:
  - url: https://vorsorgefonds.ch/api
security:
  - bearerAuth: []
paths:
  /fonds:
    get:
      summary: Säule-3a-Vorsorgefonds suchen und ranken
      description: |
        Bankfonds mit ISIN. Fonds ohne vergleichbare Kosten (NT-Tranchen mit
        Schein-TER 0.00%) sind ausgeschlossen. Standardsortierung: Kosten
        aufsteigend.
      operationId: searchFonds
      parameters:
        - $ref: '#/components/parameters/kostenMaxProzent'
        - $ref: '#/components/parameters/aktienquoteMinProzent'
        - $ref: '#/components/parameters/aktienquoteMaxProzent'
        - $ref: '#/components/parameters/nurNachhaltige'
        - $ref: '#/components/parameters/anbieter'
        - $ref: '#/components/parameters/sortierung'
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: Trefferliste
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FondsListe'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /strategien:
    get:
      summary: 3a-App-Strategien suchen und ranken
      description: >
        Anlagestrategien von Schweizer 3a-Apps (VIAC, frankly, finpension,
        Yuh, Swissquote etc.) mit All-in-Gebühren.
      operationId: searchStrategien
      parameters:
        - $ref: '#/components/parameters/kostenMaxProzent'
        - $ref: '#/components/parameters/aktienquoteMinProzent'
        - $ref: '#/components/parameters/aktienquoteMaxProzent'
        - $ref: '#/components/parameters/nurNachhaltige'
        - $ref: '#/components/parameters/anbieter'
        - $ref: '#/components/parameters/sortierung'
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: Trefferliste
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StrategienListe'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /konten:
    get:
      summary: Zinssätze für Säule-3a- und Freizügigkeitskonten
      operationId: getZinsen
      parameters:
        - name: vorsorgeArt
          in: query
          description: '"3a" = Säule 3a (Standard), "fz" = Freizügigkeit'
          schema:
            type: string
            enum: ['3a', 'fz']
            default: '3a'
        - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: Konten, nach Zins absteigend sortiert
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KontenListe'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /produkte/{idOderIsin}:
    get:
      summary: Details zu einem Produkt (Fonds, Strategie oder Konto)
      description: >
        Produkt-IDs stammen aus den Such-Endpoints; ISINs funktionieren nur
        für Fonds. Die Antwortfelder hängen von der Produktkategorie ab
        (Fonds: Factsheet, Volatilität, Kaufstellen; Strategie:
        Komponenten-Fonds, Gebührenaufschlüsselung, App-Features; Konto:
        Zins und Konditionen).
      operationId: getProduktDetails
      parameters:
        - name: idOderIsin
          in: path
          required: true
          description: Produkt-ID (z.B. "viac-global-100") oder ISIN (z.B. "CH0372701505")
          schema:
            type: string
      responses:
        '200':
          description: Produktdetails (Felder je nach Kategorie)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProduktDetails'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /vergleich:
    get:
      summary: 2–5 Produkte direkt vergleichen
      description: >
        Auch kategorieübergreifend (Fonds vs. Strategie vs. Konto); die
        Antwort enthält dann einen Hinweis zur eingeschränkten
        Kosten-Vergleichbarkeit.
      operationId: vergleicheProdukte
      parameters:
        - name: produkte
          in: query
          required: true
          description: 2–5 Produkt-IDs oder ISINs, kommasepariert
          example: viac-global-100,CH0372701505
          schema:
            type: string
      responses:
        '200':
          description: Produkte nebeneinander
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Vergleich'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /fakten:
    get:
      summary: Grundlagen zur Säule 3a und Freizügigkeit
      description: >
        Maximalbeiträge 2026, Produktkategorien mit Datenbestand,
        Steuervorteil-Hinweis und weiterführende Links.
      operationId: getFakten
      responses:
        '200':
          description: Fakten-Objekt
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >
        Early-Access-Key. Registrierung auf https://vorsorgefonds.ch/api –
        der Key wird per E-Mail zugestellt.
  parameters:
    kostenMaxProzent:
      name: kostenMaxProzent
      in: query
      description: Maximale jährliche Kosten in % (z.B. 0.5)
      schema:
        type: number
    aktienquoteMinProzent:
      name: aktienquoteMinProzent
      in: query
      description: Minimale Aktienquote in %
      schema:
        type: number
    aktienquoteMaxProzent:
      name: aktienquoteMaxProzent
      in: query
      description: Maximale Aktienquote in %
      schema:
        type: number
    nurNachhaltige:
      name: nurNachhaltige
      in: query
      description: Nur ESG-/nachhaltige Produkte
      schema:
        type: string
        enum: ['true', 'false', '1', '0']
    anbieter:
      name: anbieter
      in: query
      description: Filtert auf Anbieternamen (Teilstring, z.B. "Migros" oder "viac")
      schema:
        type: string
    sortierung:
      name: sortierung
      in: query
      description: >
        "kosten" aufsteigend (Standard) oder Rendite absteigend; Produkte
        ohne Daten für die gewählte Periode fallen ans Ende.
      schema:
        type: string
        enum: [kosten, rendite-1y, rendite-3y, rendite-5y]
        default: kosten
    limit:
      name: limit
      in: query
      description: Maximale Anzahl Ergebnisse
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
  responses:
    BadRequest:
      description: Ungültige Query-Parameter
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Fehler'
    Unauthorized:
      description: Fehlender oder ungültiger Early-Access-Key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Fehler'
    NotFound:
      description: Produkt nicht gefunden
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Fehler'
  schemas:
    Fehler:
      type: object
      properties:
        fehler:
          type: string
        hinweis:
          type: string
        details:
          description: Validierungsdetails (nur bei 400)
      required: [fehler]
    Rendite:
      type: object
      description: Rendite in % je Zeitraum; 3J/5J annualisiert. Fehlende Werte = keine Daten.
      properties:
        ytd:
          type: number
        '1y':
          type: number
        3yPa:
          type: number
        5yPa:
          type: number
    Fonds:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        anbieter:
          type: string
        isin:
          type: string
        kostenProJahrProzent:
          type: number
          description: Effektive Jahreskosten inkl. TER (günstigster Anbieter)
        aktienquoteProzent:
          type: number
        nachhaltig:
          type: boolean
        renditeProzent:
          $ref: '#/components/schemas/Rendite'
        url:
          type: string
          format: uri
      required: [id, name, anbieter, kostenProJahrProzent, aktienquoteProzent, nachhaltig, url]
    Strategie:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        anbieter:
          type: string
        allInGebuehrenProzent:
          type: number
          description: All-in-Gebühren (Verwaltung + gewichtete Produktkosten)
        aktienquoteProzent:
          type: number
        nachhaltig:
          type: boolean
        renditeProzent:
          $ref: '#/components/schemas/Rendite'
        url:
          type: string
          format: uri
      required: [id, name, anbieter, allInGebuehrenProzent, aktienquoteProzent, nachhaltig, url]
    Konto:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        anbieter:
          type: string
        zinsProzent:
          type: number
        bedingungen:
          type: boolean
          description: true, wenn Bedingungen oder Kundenvoraussetzungen gelten
        hinweis:
          type: string
        produktlink:
          type: string
          format: uri
      required: [id, name, anbieter, zinsProzent, produktlink]
    FondsListe:
      type: object
      properties:
        anzahlTreffer:
          type: integer
        anzahlZurueckgegeben:
          type: integer
        hinweis:
          type: string
        fonds:
          type: array
          items:
            $ref: '#/components/schemas/Fonds'
        quelle:
          type: string
        disclaimer:
          type: string
      required: [anzahlTreffer, anzahlZurueckgegeben, fonds, quelle, disclaimer]
    StrategienListe:
      type: object
      properties:
        anzahlTreffer:
          type: integer
        anzahlZurueckgegeben:
          type: integer
        strategien:
          type: array
          items:
            $ref: '#/components/schemas/Strategie'
        quelle:
          type: string
        disclaimer:
          type: string
      required: [anzahlTreffer, anzahlZurueckgegeben, strategien, quelle, disclaimer]
    KontenListe:
      type: object
      properties:
        vorsorgeArt:
          type: string
          enum: ['3a', 'fz']
        anzahlKonten:
          type: integer
        konten:
          type: array
          items:
            $ref: '#/components/schemas/Konto'
        quelle:
          type: string
        disclaimer:
          type: string
      required: [vorsorgeArt, anzahlKonten, konten, quelle, disclaimer]
    ProduktDetails:
      type: object
      description: >
        Felder abhängig von der Produktkategorie ("Vorsorgefonds (Bankfonds)",
        "3a-App-Strategie", "Säule-3a-Konto" oder "Freizügigkeitskonto").
      properties:
        kategorie:
          type: string
        id:
          type: string
        name:
          type: string
        anbieter:
          type: string
        url:
          type: string
          format: uri
        quelle:
          type: string
        disclaimer:
          type: string
      required: [kategorie, id, name, anbieter, url]
      additionalProperties: true
    VergleichsProdukt:
      type: object
      properties:
        kategorie:
          type: string
          description: Vorsorgefonds, 3a-App-Strategie oder Vorsorgekonto
        id:
          type: string
        name:
          type: string
        anbieter:
          type: string
        isin:
          type: string
        kostenProJahrProzent:
          type: number
          description: Fehlt bei Konten
        zinsProzent:
          type: number
          description: Nur bei Konten
        aktienquoteProzent:
          type: number
        risikoLevel:
          type: integer
          minimum: 1
          maximum: 7
        nachhaltig:
          type: boolean
        renditeProzent:
          $ref: '#/components/schemas/Rendite'
        url:
          type: string
          format: uri
      required: [kategorie, id, name, anbieter, nachhaltig, url]
    Vergleich:
      type: object
      properties:
        hinweis:
          type: string
          description: Gesetzt bei kategorieübergreifendem Vergleich
        produkte:
          type: array
          items:
            $ref: '#/components/schemas/VergleichsProdukt'
        nichtGefunden:
          type: array
          items:
            type: string
        quelle:
          type: string
        disclaimer:
          type: string
      required: [produkte, quelle, disclaimer]
