openapi: 3.1.0
info:
  title: Boomki Boo API
  version: "1.0"
  license:
    name: Proprietär — Nutzung gemäß Boomki-Vertrag
    url: https://boomki.app/imprint
  description: |
    Boo — der Maschinenwissens-Assistent von Boomki — per HTTP fragen.
    API-Keys werden in den Workspace-Einstellungen erzeugt (nur Owner/Admin)
    und genau einmal angezeigt. Ein Key hat Zugriff auf das gesamte
    Maschinenwissen seines Workspace. Fragen zählen wie Web-Fragen in
    Kontingent und Abrechnung.
servers:
  - url: https://boomki.app
security:
  - apiKey: []
paths:
  /api/v1/ask:
    post:
      operationId: askBoo
      summary: Boo eine Frage zum Maschinenwissen stellen
      description: |
        Beantwortet eine Technikerfrage aus dem gespeicherten Maschinenwissen
        (Protokolle, Dokumente, Interviews, Aufnahmen). Maschine per
        machineId (stabil, empfohlen) oder machine (Name, eindeutiger
        Teilstring genügt); ohne beides wird über den gesamten Maschinenpark
        gesucht.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [question]
              properties:
                question:
                  type: string
                  minLength: 1
                  maxLength: 2000
                  description: Die Frage des Technikers.
                machineId:
                  type: string
                  description: Exakte Maschinen-Id (siehe /api/v1/machines).
                machine:
                  type: string
                  description: Alternativ der Maschinenname (case-insensitiv).
                lang:
                  type: string
                  enum: [de, en]
                  default: de
                  description: Antwortsprache.
      responses:
        "200":
          description: Antwort mit Quellen und Konfidenz.
          content:
            application/json:
              schema:
                type: object
                properties:
                  answer:
                    type: string
                    description: Die Antwort, werkstatt-tauglich formuliert.
                  steps:
                    type: array
                    items: { type: string }
                    description: Nummerierte Arbeitsschritte (falls sinnvoll).
                  sources:
                    type: array
                    description: Belegte Quellen (nur solche, die die Antwort stützen).
                    items:
                      type: object
                      properties:
                        label: { type: string }
                        type:
                          type: string
                          enum: [interview, tip, protocol, recording, document, chat]
                        machine:
                          type: string
                          description: Nur bei parkweiter Suche gesetzt.
                  confidence:
                    type: string
                    enum: [high, medium, low]
                    description: >
                      high = mehrere unabhängige Quellen; medium = eine klare
                      Quelle; low = nur indirekte Hinweise (vorsichtig
                      behandeln).
                  machine:
                    # OpenAPI 3.1 = JSON Schema: null gehört in type, nicht in
                    # das (dort ungültige) nullable-Flag.
                    type: [object, "null"]
                    description: Die aufgelöste Maschine; null bei parkweiter Suche.
                    properties:
                      id: { type: string }
                      name: { type: string }
        "400":
          description: Ungültiger Request-Body (error=invalid_request, detail).
        "401":
          description: Key fehlt, unbekannt oder widerrufen (error=invalid_key).
        "402":
          description: Free-Kontingent erschöpft (error=ask_limit_reached).
        "404":
          description: Maschine nicht gefunden (error=machine_not_found).
        "422":
          description: Maschinenname mehrdeutig (error=ambiguous_machine, candidates).
        "429":
          description: Zu viele Anfragen — Retry-After-Header beachten (error=rate_limited).
  /api/v1/protocols:
    post:
      operationId: createProtocol
      summary: Wartungs-/Störungsprotokoll anlegen
      description: |
        Legt ein Protokoll an — aus Freitext (KI füllt die Vorlagen-Felder)
        oder direkten Feldwerten. Default ist ein ENTWURF; submit=true löst
        die volle Kette aus (Zähler, Export nach SharePoint/Nextcloud,
        SAP-Meldung, Teams-Benachrichtigung).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [template]
              properties:
                template:
                  type: string
                  description: '"fault", "maintenance", "safety" oder die Id einer eigenen Vorlage.'
                machineId: { type: string }
                machine:
                  type: string
                  description: Alternativ der Maschinenname (eindeutiger Teilstring genügt).
                text:
                  type: string
                  maxLength: 20000
                  description: Freitext/Transkript — die KI füllt die Felder.
                values:
                  type: object
                  additionalProperties: { type: string }
                  description: Direkte Feldwerte (gewinnen je Key über den KI-Fill).
                submit:
                  type: boolean
                  default: false
                  description: true = direkt abschließen (Export/SAP/Teams laufen an).
      responses:
        "201":
          description: Angelegt.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: string }
                  status: { type: string, enum: [draft, submitted] }
                  machine:
                    type: [object, "null"]
                    properties:
                      id: { type: string }
                      name: { type: string }
                  values:
                    type: object
                    additionalProperties: { type: string }
        "400": { description: Ungültiger Request-Body (error=invalid_request). }
        "401": { description: Key ungültig (error=invalid_key). }
        "402": { description: Protokoll-Kontingent erschöpft (error=protocol_limit_reached). }
        "404": { description: Maschine nicht gefunden (error=machine_not_found). }
        "422": { description: "unknown_template, invalid_fields oder ambiguous_machine." }
  /api/v1/machines:
    get:
      operationId: listMachines
      summary: Maschinen des Workspace auflisten
      description: |
        Liefert alle Maschinen — für stabile machineId-Aufrufe und zum Lesen
        der Anlagenstruktur: parentId/parentName zeigen, wessen Teil eine
        Maschine ist ("ist Teil von", z.B. Equipment → Teilanlage → Anlage).
      responses:
        "200":
          description: Maschinenliste.
          content:
            application/json:
              schema:
                type: object
                properties:
                  machines:
                    type: array
                    items:
                      type: object
                      properties:
                        id: { type: string }
                        name: { type: string }
                        location:
                          type: [string, "null"]
                          description: Standort als Freitext (z.B. "Werk Münster, Halle 2").
                        parentId:
                          type: [string, "null"]
                          description: id der übergeordneten Maschine, null = eigenständig.
                        parentName:
                          type: [string, "null"]
        "401":
          description: Key fehlt, unbekannt oder widerrufen (error=invalid_key).
    post:
      operationId: createMachine
      summary: Eine Maschine anlegen
      description: |
        Legt eine Maschine im Workspace an — für Import-Skripte und externe
        Systeme. Über parentId (aus GET /api/v1/machines) hängt die neue
        Maschine direkt in der Anlagenstruktur ("ist Teil von").

        Dubletten-Schutz ist Standard: existiert bereits eine Maschine mit
        demselben Namen (Groß-/Kleinschreibung egal), antwortet die API mit
        409 und der bestehenden id — ein wiederholter Import-Lauf erzeugt so
        keine Doppelgänger. Wer bewusst zwei gleichnamige Maschinen will,
        setzt allowDuplicateName.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  maxLength: 120
                manufacturer: { type: string, maxLength: 120 }
                model: { type: string, maxLength: 120 }
                location:
                  type: string
                  maxLength: 120
                  description: Standort als Freitext (z.B. "Werk Münster, Halle 2").
                inventoryNumber: { type: string, maxLength: 120 }
                serialNumber: { type: string, maxLength: 120 }
                buildYear: { type: integer, minimum: 1800, maximum: 2100 }
                costCenter: { type: string, maxLength: 120 }
                parentId:
                  type: string
                  description: id der übergeordneten Maschine (aus GET /api/v1/machines).
                allowDuplicateName:
                  type: boolean
                  default: false
      responses:
        "201":
          description: Maschine angelegt.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: string }
                  name: { type: string }
                  parent:
                    type: [object, "null"]
                    properties:
                      id: { type: string }
                      name: { type: string }
        "400": { description: Ungültiger Request-Body (error=invalid_request). }
        "401": { description: "Key fehlt, unbekannt oder widerrufen (error=invalid_key)." }
        "409":
          description: |
            Gleichnamige Maschine existiert bereits (error=duplicate_name,
            machine={id,name}) — deren id weiterverwenden oder
            allowDuplicateName setzen.
        "422": { description: parentId unbekannt (error=parent_not_found). }
  /api/v1/maintenance:
    post:
      operationId: planMaintenance
      summary: Eine Wartung einplanen
      description: |
        Legt eine geplante Wartung an einer Maschine an — für Instandhaltungs-
        planung, die anderswo läuft (SAP PM, Tabelle, eigenes Werkzeug).

        Die Vorlage (`template`) ist OPTIONAL. Ohne sie wird beim Starten der
        Wartung gefragt, welcher Bogen gilt. Das ist die richtige Wahl, wenn das
        aufrufende System die Wartungsbögen der Firma nicht kennt — eine still
        gesetzte Vorlage wäre ein Prüfnachweis mit dem falschen Formular.

        Abgehakt wird eine Wartung nicht über die API, sondern dort, wo sie
        gemacht wird: in der App oder über Telegram.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title, dueDate]
              properties:
                machineId:
                  type: string
                  description: Kennung der Maschine (aus /api/v1/machines). Genauer als der Name.
                machine:
                  type: string
                  description: |
                    Name der Maschine, falls keine Kennung vorliegt. Mehrdeutige
                    Namen werden nicht geraten, sondern mit 422 abgelehnt.
                title:
                  type: string
                  maxLength: 200
                  description: Was zu tun ist, z. B. "Ölwechsel Spindel".
                template:
                  type: [string, "null"]
                  description: |
                    Kennung des Wartungsbogens. Weglassen oder null = beim
                    Starten fragen. Unbekannte Vorlagen sind 422.
                assignedTo:
                  type: [string, "null"]
                  description: |
                    Zuständige Person — Nutzerkennung ODER E-Mail-Adresse. Muss
                    Mitglied der Firma sein.
                dueDate:
                  type: string
                  description: |
                    Fälligkeit, als Datum ("2026-08-25") oder voller Zeitpunkt
                    ("2026-08-25T08:00:00Z"). Ein reines Datum gilt als
                    Mitternacht UTC und liegt damit auf demselben Kalendertag.
                recurrence:
                  type: string
                  enum: [none, weekly, monthly, quarterly, yearly]
                  default: none
                  description: |
                    Wiederholung. Die Folgeaufgabe entsteht erst beim Abhaken,
                    nicht im Voraus.
      responses:
        "201":
          description: Wartung eingeplant.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: string }
                  title: { type: string }
                  machine:
                    type: object
                    properties:
                      id: { type: string }
                      name: { type: string }
                  template: { type: [string, "null"] }
                  dueDate: { type: string }
                  recurrence: { type: string }
                  assignedTo: { type: [string, "null"] }
        "400": { description: Fehlerhafte Anfrage oder unlesbares dueDate (error=invalid_request). }
        "401": { description: Key ungültig (error=invalid_key). }
        "404": { description: Maschine nicht gefunden (error=machine_not_found). }
        "422": { description: "unknown_template, ambiguous_machine oder unknown_assignee." }
    get:
      operationId: listMaintenance
      summary: Geplante Wartungen auflisten
      description: |
        Vorgabe sind die OFFENEN Wartungen, nach Fälligkeit sortiert. Ohne diesen
        Weg könnte ein fremdes System nicht erkennen, ob eine Wartung inzwischen
        gemacht wurde, und würde sie doppelt einplanen. Höchstens 200 Einträge.
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, completed, cancelled, all]
          description: Vorgabe pending.
        - name: machineId
          in: query
          schema: { type: string }
          description: Nur Wartungen dieser Maschine.
      responses:
        "200":
          description: Liste der Wartungen.
          content:
            application/json:
              schema:
                type: object
                properties:
                  tasks:
                    type: array
                    items:
                      type: object
                      properties:
                        id: { type: string }
                        title: { type: string }
                        machineId: { type: string }
                        machineName: { type: string }
                        template: { type: [string, "null"] }
                        dueDate: { type: string }
                        recurrence: { type: string }
                        status: { type: string }
                        assignedTo: { type: [string, "null"] }
        "400": { description: Unbekannter status-Wert (error=invalid_request). }
        "401": { description: Key ungültig (error=invalid_key). }
  /api/v1/templates:
    get:
      operationId: listTemplates
      summary: Protokollvorlagen auflisten
      description: |
        Liefert die Kennungen, die als `template` in POST /api/v1/maintenance und
        POST /api/v1/protocols eingesetzt werden können. Ohne diesen Weg wäre das
        Feld praktisch unbenutzbar — eingebaute Kennungen sind über alle Firmen
        gleich, eigene sind UUIDs.

        Archivierte und abgelöste Vorlagen fehlen bewusst: ein fremdes System
        soll keine Fassung hinterlegen, die die Firma längst ersetzt hat.
      responses:
        "200":
          description: Vorlagenliste, eingebaute zuerst, eigene danach nach Namen.
          content:
            application/json:
              schema:
                type: object
                properties:
                  templates:
                    type: array
                    items:
                      type: object
                      properties:
                        id: { type: string }
                        name: { type: string }
                        description: { type: [string, "null"] }
                        kind:
                          type: string
                          enum: [builtin, custom]
                        fieldCount: { type: integer }
        "401":
          description: Key fehlt, unbekannt oder widerrufen (error=invalid_key).
components:
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: "API-Key aus den Workspace-Einstellungen: Authorization: Bearer bk_live_…"
