openapi: 3.1.0
info:
  title: firmenliste.net Agent API
  version: "0.5"
  description: >
    Maschinenlesbare Schnittstelle für KI-Agenten (Stufe 1: read-only).
    Ablauf: (1) /branchen findet passende Adresslisten zu einem Suchbegriff
    — inkl. Anzahl und Preis, (2) /count liefert Details einer Liste
    (Feld-Verfügbarkeit, Bundesland-Verteilung), (3) /quote erzeugt ein
    verbindliches, 48h gültiges Angebot für die komplette Liste,
    (4) /terms liefert die Kauf- und Nutzungsbedingungen, (5) /laender
    listet alle verfügbaren Länder. Die Bestellung erfolgt derzeit über
    die Website (bestell_url im Quote).
  termsOfService: https://www.firmenliste.net/api/v1/terms
  contact:
    email: support@firmenliste.net

servers:
  - url: https://www.firmenliste.net/api/v1

paths:
  /laender:
    get:
      operationId: listLaender
      summary: Verfügbare Länder auflisten (online kaufbar oder auf Anfrage)
      description: >
        DACH-Länder mit online kaufbaren Listen (inkl. Listen- und
        Datensatzanzahl) plus rund 40 weitere Länder, deren Daten auf
        Anfrage verfügbar sind.
      responses:
        "200":
          description: Länderliste
          content:
            application/json:
              schema:
                type: object
                properties:
                  laender:
                    type: array
                    items:
                      type: object
                      properties:
                        code: { type: string, example: "de" }
                        name: { type: string, example: "Deutschland" }
                        online_bestellbar: { type: boolean }
                        anzahl_listen:
                          type: integer
                          example: 4500
                          description: Nur bei online bestellbaren Ländern
                        anzahl_datensaetze: { type: integer, example: 8100000 }
                        hinweis: { type: string, example: "auf Anfrage" }
                        stand: { type: string, format: date, example: "2025-09-30" }

  /branchen:
    get:
      operationId: matchBranchen
      summary: Adresslisten zu einem Suchbegriff finden (mit Anzahl und Preis)
      description: >
        Durchsucht die Branchenlisten nach Name, Keywords und
        Unterkategorien. Liefert pro Treffer bereits Datensatzanzahl,
        Feld-Verfügbarkeit und den Komplettpreis — für viele
        Agenten-Anfragen reicht dieser eine Call.
      parameters:
        - name: q
          in: query
          required: true
          schema: { type: string, minLength: 3 }
          description: Suchbegriff, z. B. "Golf" oder "Inkasso"
        - name: land
          in: query
          description: >
            de, at oder ch (Online-Katalog). Andere Ländercodes liefern
            eine leere Trefferliste mit Hinweis auf den Anfrage-Weg.
          schema: { type: string, default: de }
        - name: limit
          in: query
          schema: { type: integer, default: 20, maximum: 50 }
      responses:
        "200":
          description: Trefferliste, sortiert nach Datensatzanzahl
          content:
            application/json:
              schema:
                type: object
                properties:
                  treffer:
                    type: array
                    items:
                      type: object
                      properties:
                        idListe: { type: integer, example: 16 }
                        name: { type: string, example: "Golfclubs" }
                        land: { type: string, example: "de" }
                        anzahl: { type: integer, example: 881 }
                        mit_email: { type: integer, example: 699 }
                        mit_telefon: { type: integer, example: 881 }
                        mit_website: { type: integer, example: 797 }
                        mit_ansprechpartner: { type: integer, example: 670 }
                        online_bestellbar: { type: boolean }
                        preis_komplett_netto_eur:
                          type: [number, "null"]
                          example: 100
                          description: Nettopreis des Komplettpakets; null = nur auf Anfrage
                        stand: { type: string, format: date, example: "2025-09-30" }

  /count:
    get:
      operationId: getListe
      summary: Detailansicht einer Liste
      description: >
        Alle Verfügbarkeitszahlen einer Liste inkl. Bundesland-Verteilung,
        enthaltener Datenfelder und Social-Media-Abdeckung.
      parameters:
        - name: idListe
          in: query
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Listendetails
          content:
            application/json:
              schema:
                type: object
                properties:
                  idListe: { type: integer, example: 16 }
                  name: { type: string, example: "Golfclubs" }
                  land: { type: string, example: "de" }
                  total: { type: integer, example: 881 }
                  mit_adresse: { type: integer, example: 881 }
                  mit_telefon: { type: integer, example: 881 }
                  mit_email: { type: integer, example: 699 }
                  mit_website: { type: integer, example: 797 }
                  mit_ansprechpartner: { type: integer, example: 670 }
                  mit_fax: { type: integer, example: 192 }
                  mit_umsatz: { type: integer, example: 668 }
                  mit_mitarbeiterzahl: { type: integer, example: 668 }
                  mit_social_media:
                    type: object
                    properties:
                      facebook: { type: integer, example: 461 }
                      instagram: { type: integer, example: 372 }
                      linkedin: { type: integer, example: 22 }
                  bundeslaender:
                    type: object
                    additionalProperties: { type: integer }
                    example: { "Bayern": 204, "Nordrhein-Westfalen": 183 }
                  enthaltene_felder:
                    type: array
                    items: { type: string }
                  preise:
                    type: [object, "null"]
                    description: Onlinepreise (netto); null = nur auf Anfrage
                    properties:
                      komplett_netto_eur: { type: number, example: 250 }
                      nur_email_netto_eur: { type: number, example: 180 }
                      bundesland_pakete:
                        type: object
                        additionalProperties:
                          type: object
                          properties:
                            netto_eur: { type: number }
                            adressen: { type: integer }
                            emails: { type: integer }
                  online_bestellbar: { type: boolean }
                  stand: { type: string, format: date, example: "2025-09-30" }
        "404":
          description: Liste nicht gefunden oder nicht verfügbar

  /quote:
    post:
      operationId: createQuote
      summary: Verbindliches Angebot für eine komplette Liste erzeugen
      description: >
        Erzeugt ein 48h gültiges, preisstabiles Angebot mit quote_id.
        Pakete wie im Shop: Komplettpaket (Standard), Nur-E-Mail-Paket
        oder einzelnes Bundesland. Nettopreise zzgl. deutscher MwSt.
        Die bestell_url führt zur Produktseite mit Warenkorb/Checkout.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [idListe]
              properties:
                idListe: { type: integer, example: 16 }
                umfang:
                  type: string
                  enum: [komplett, nur_email]
                  default: komplett
                  description: nur_email = Liste mit E-Mail, Kategorie und Bundesland
                bundesland:
                  type: string
                  description: Optional, z. B. "Bayern" — bepreist nur dieses Bundesland-Paket
      responses:
        "200":
          description: Angebot
          content:
            application/json:
              schema:
                type: object
                properties:
                  quote_id: { type: string, example: "q_7f3a91c2" }
                  idListe: { type: integer }
                  liste_name: { type: string, example: "Golfclubs" }
                  land: { type: string, example: "de" }
                  paket: { type: string, example: "Komplettpaket" }
                  umfang: { type: string, example: "komplett" }
                  menge: { type: integer, example: 881 }
                  mit_email: { type: integer, example: 699 }
                  mit_telefon: { type: integer, example: 881 }
                  enthaltene_felder:
                    type: array
                    items: { type: string }
                  preis_netto_eur: { type: number, example: 100.00 }
                  mwst_satz: { type: number, example: 0.19 }
                  preis_brutto_eur: { type: number, example: 119.00 }
                  lieferformat: { type: string, example: "CSV oder XLSX" }
                  lieferzeit: { type: string, example: "sofort nach Zahlungseingang" }
                  stand: { type: string, format: date }
                  gueltig_bis: { type: string, format: date-time }
                  bestell_url: { type: string }
        "404":
          description: Liste nicht gefunden oder nicht verfügbar
        "422":
          description: >
            Nicht online bestellbar (Antwort enthält anfrage_url) oder
            unbekanntes Bundesland (Antwort enthält verfuegbare_bundeslaender)

  /terms:
    get:
      operationId: getTerms
      summary: Maschinenlesbare Kauf- und Nutzungsbedingungen
      responses:
        "200":
          description: Konditionen
          content:
            application/json:
              schema:
                type: object
                properties:
                  b2b_only: { type: boolean }
                  mehrfachnutzung: { type: boolean }
                  nutzungsdauer: { type: string }
                  widerruf: { type: string }
                  mwst: { type: string }
                  datenquellen: { type: string }
                  rechtlicher_hinweis: { type: string }
                  agb_url: { type: string }
