{"openapi":"3.1.0","info":{"title":"Hainsoft API","version":"1.0.0","description":"REST API für die Integration des Hainsoft-Systems mit externen Anwendungen.\n\nAlle Endpunkte sind **mandantenfähig** – der API-Schlüssel bestimmt automatisch, auf welchen Mandanten zugegriffen wird.\n\nAPI-Schlüssel können unter **Einstellungen → API** erstellt werden.","contact":{"name":"Hainsoft Support","email":"support@hainsoft.io","url":"https://hainsoft.io"}},"servers":[{"url":"https://hainsoft.io","description":"Produktion"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Kunden","description":"Kundenstammdaten"},{"name":"Mitarbeiter","description":"Aktive Mitarbeiter des Mandanten"},{"name":"Projekte","description":"Bauprojekte und Aufträge"},{"name":"Angebote","description":"Angebotsverwaltung"},{"name":"Rechnungen","description":"Ausgangsrechnungen"},{"name":"Zeiterfassung","description":"Arbeitszeiteinträge"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API-Schlüssel aus hainsoft (Einstellungen → API). Format: `hs_live_...`"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","example":"Unauthorized"}}},"PaginatedList":{"type":"object","properties":{"total":{"type":"integer","description":"Gesamtanzahl aller Datensätze"},"limit":{"type":"integer","description":"Maximale Anzahl in dieser Antwort"},"offset":{"type":"integer","description":"Pagination-Offset"}}},"CustomerRef":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"companyName":{"type":"string","nullable":true},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true}}},"ProjectRef":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"projectNumber":{"type":"string","nullable":true},"name":{"type":"string"}}},"Customer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"companyName":{"type":"string","nullable":true,"example":"Muster GmbH"},"firstName":{"type":"string","nullable":true,"example":"Max"},"lastName":{"type":"string","nullable":true,"example":"Mustermann"},"email":{"type":"string","format":"email","nullable":true},"phone":{"type":"string","nullable":true},"street":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"customerNr":{"type":"string","nullable":true,"example":"K-0042"},"createdAt":{"type":"string","format":"date-time"},"_count":{"type":"object","properties":{"projects":{"type":"integer"},"invoices":{"type":"integer"}}}}},"Employee":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"firstName":{"type":"string"},"lastName":{"type":"string"},"employmentType":{"type":"string","enum":["FULL_TIME","PART_TIME","MINI_JOB","CONTRACTOR"],"nullable":true},"weeklyHours":{"type":"number","nullable":true},"qualifications":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"}}},"Invoice":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"invoiceNumber":{"type":"string","example":"RE-2025-0042"},"status":{"type":"string","enum":["DRAFT","SENT","PARTIAL_PAID","PAID","OVERDUE","CANCELLED"]},"issueDate":{"type":"string","format":"date-time"},"dueDate":{"type":"string","format":"date-time","nullable":true},"netAmount":{"type":"number","format":"double","example":1000},"grossAmount":{"type":"number","format":"double","example":1190},"paidAmount":{"type":"number","format":"double","example":0},"createdAt":{"type":"string","format":"date-time"},"customer":{"$ref":"#/components/schemas/CustomerRef","nullable":true},"project":{"$ref":"#/components/schemas/ProjectRef","nullable":true}}},"Offer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"offerNumber":{"type":"string","example":"ANG-2025-0017"},"status":{"type":"string","enum":["DRAFT","SENT","ACCEPTED","REJECTED","EXPIRED","INVOICED"]},"issueDate":{"type":"string","format":"date-time"},"validUntil":{"type":"string","format":"date-time","nullable":true},"netAmount":{"type":"number","format":"double","nullable":true},"grossAmount":{"type":"number","format":"double","nullable":true},"createdAt":{"type":"string","format":"date-time"},"customer":{"$ref":"#/components/schemas/CustomerRef","nullable":true},"project":{"$ref":"#/components/schemas/ProjectRef","nullable":true}}},"Project":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"projectNumber":{"type":"string","nullable":true,"example":"P-2025-0033"},"name":{"type":"string"},"status":{"type":"string","enum":["PLANNED","TENDERING","OFFER_SENT","ORDERED","IN_PROGRESS","PAUSED","COMPLETED","INVOICED","CANCELLED"]},"progressPercent":{"type":"integer","minimum":0,"maximum":100,"nullable":true},"budgetAmount":{"type":"number","format":"double","nullable":true},"plannedStartDate":{"type":"string","format":"date-time","nullable":true},"plannedEndDate":{"type":"string","format":"date-time","nullable":true},"actualStartDate":{"type":"string","format":"date-time","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"customer":{"$ref":"#/components/schemas/CustomerRef","nullable":true}}},"ProjectDetail":{"allOf":[{"$ref":"#/components/schemas/Project"},{"type":"object","properties":{"description":{"type":"string","nullable":true},"actualEndDate":{"type":"string","format":"date-time","nullable":true},"customer":{"allOf":[{"$ref":"#/components/schemas/CustomerRef"},{"type":"object","properties":{"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true}}}],"nullable":true},"_count":{"type":"object","properties":{"timeEntries":{"type":"integer"},"documents":{"type":"integer"},"invoices":{"type":"integer"}}}}}]},"TimeEntry":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"workDate":{"type":"string","format":"date-time"},"startTime":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"date-time"},"workMinutes":{"type":"integer","description":"Netto-Arbeitsminuten (ohne Pause)"},"breakMinutes":{"type":"integer"},"status":{"type":"string","enum":["DRAFT","SUBMITTED","APPROVED","REJECTED"]},"activity":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"employee":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"project":{"$ref":"#/components/schemas/ProjectRef","nullable":true}}}}},"paths":{"/api/v1/customers":{"get":{"tags":["Kunden"],"summary":"Kundenliste abrufen","description":"Gibt eine paginierte Liste aller Kunden des Mandanten zurück.","operationId":"listCustomers","parameters":[{"name":"q","in":"query","description":"Freitext-Suche (Firma, Vor-/Nachname, E-Mail)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max. Anzahl (Standard: 50, Max: 200)","schema":{"type":"integer","default":50,"maximum":200}},{"name":"offset","in":"query","description":"Pagination-Offset","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Liste der Kunden","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedList"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Customer"}}}}]},"example":{"data":[{"id":"uuid-...","companyName":"Muster GmbH","firstName":null,"lastName":null,"email":"info@muster.de","customerNr":"K-0042"}],"total":1,"limit":50,"offset":0}}}},"401":{"description":"Fehlender oder ungültiger API-Schlüssel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/employees":{"get":{"tags":["Mitarbeiter"],"summary":"Mitarbeiterliste abrufen","description":"Gibt alle aktiven Mitarbeiter zurück. Stundensätze werden aus Datenschutzgründen nicht übermittelt.","operationId":"listEmployees","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":100,"maximum":500}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Liste der Mitarbeiter","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedList"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Employee"}}}}]}}}},"401":{"description":"Fehlender oder ungültiger API-Schlüssel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/offers":{"get":{"tags":["Angebote"],"summary":"Angebotsliste abrufen","description":"Gibt eine paginierte Liste der Angebote zurück, filterbar nach Status.","operationId":"listOffers","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["DRAFT","SENT","ACCEPTED","REJECTED","EXPIRED","INVOICED"]}},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":200}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Liste der Angebote","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedList"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Offer"}}}}]}}}},"401":{"description":"Fehlender oder ungültiger API-Schlüssel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/invoices":{"get":{"tags":["Rechnungen"],"summary":"Rechnungsliste abrufen","description":"Gibt eine paginierte Liste der Rechnungen zurück. Filterbar nach Status und Ausstellungsdatum.","operationId":"listInvoices","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["DRAFT","SENT","PARTIAL_PAID","PAID","OVERDUE","CANCELLED"]}},{"name":"from","in":"query","description":"Ausstellungsdatum ab (ISO-8601)","schema":{"type":"string","format":"date","example":"2025-01-01"}},{"name":"to","in":"query","description":"Ausstellungsdatum bis (ISO-8601)","schema":{"type":"string","format":"date","example":"2025-12-31"}},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":200}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Liste der Rechnungen","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedList"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}}}}]}}}},"401":{"description":"Fehlender oder ungültiger API-Schlüssel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/projects":{"get":{"tags":["Projekte"],"summary":"Projektliste abrufen","description":"Gibt eine paginierte Liste der Bauprojekte zurück, filterbar nach Status.","operationId":"listProjects","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["PLANNED","TENDERING","OFFER_SENT","ORDERED","IN_PROGRESS","PAUSED","COMPLETED","INVOICED","CANCELLED"]}},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":200}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Liste der Projekte","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedList"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Project"}}}}]}}}},"401":{"description":"Fehlender oder ungültiger API-Schlüssel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/projects/{id}":{"get":{"tags":["Projekte"],"summary":"Projekt-Details abrufen","description":"Gibt vollständige Details eines einzelnen Projekts zurück, inkl. Auftraggeber und Zählern.","operationId":"getProject","parameters":[{"name":"id","in":"path","required":true,"description":"Projekt-UUID","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Projekt-Details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDetail"}}}},"401":{"description":"Fehlender oder ungültiger API-Schlüssel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Projekt nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Projekte"],"summary":"Projekt aktualisieren","description":"Aktualisiert Status, Fortschritt, Name oder Notizen eines Projekts. Mindestens ein Feld ist erforderlich.","operationId":"updateProject","parameters":[{"name":"id","in":"path","required":true,"description":"Projekt-UUID","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","minProperties":1,"properties":{"status":{"type":"string","enum":["PLANNED","TENDERING","OFFER_SENT","ORDERED","IN_PROGRESS","PAUSED","COMPLETED","INVOICED","CANCELLED"]},"progressPercent":{"type":"integer","minimum":0,"maximum":100,"example":65},"name":{"type":"string","example":"Außenanlage Muster GmbH – Erweiterung"},"notes":{"type":"string"}}},"example":{"status":"IN_PROGRESS","progressPercent":65}}}},"responses":{"200":{"description":"Aktualisiertes Projekt","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"projectNumber":{"type":"string","nullable":true},"name":{"type":"string"},"status":{"type":"string"},"progressPercent":{"type":"integer","nullable":true},"updatedAt":{"type":"string","format":"date-time"}}}}}},"400":{"description":"Keine änderbaren Felder / ungültiger Status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Fehlender oder ungültiger API-Schlüssel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Projekt nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/time-entries":{"get":{"tags":["Zeiterfassung"],"summary":"Zeiteinträge abrufen","description":"Gibt eine paginierte Liste der Zeiterfassungen zurück, filterbar nach Datum, Mitarbeiter und Projekt.","operationId":"listTimeEntries","parameters":[{"name":"from","in":"query","description":"Arbeitsdatum ab (ISO-8601)","schema":{"type":"string","format":"date","example":"2025-05-01"}},{"name":"to","in":"query","description":"Arbeitsdatum bis (ISO-8601)","schema":{"type":"string","format":"date","example":"2025-05-31"}},{"name":"employeeId","in":"query","description":"Filter nach Mitarbeiter-UUID","schema":{"type":"string","format":"uuid"}},{"name":"projectId","in":"query","description":"Filter nach Projekt-UUID","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","schema":{"type":"integer","default":100,"maximum":500}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Liste der Zeiteinträge","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PaginatedList"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TimeEntry"}}}}]}}}},"401":{"description":"Fehlender oder ungültiger API-Schlüssel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Zeiterfassung"],"summary":"Zeiteintrag anlegen","description":"Legt einen neuen Zeiteintrag für einen Mitarbeiter an. Der Eintrag wird im Status `DRAFT` erstellt.","operationId":"createTimeEntry","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["employeeId","workDate","startTime","endTime"],"properties":{"employeeId":{"type":"string","format":"uuid","description":"Mitarbeiter-UUID (aus GET /api/v1/employees)"},"projectId":{"type":"string","format":"uuid","description":"Optional: Projekt-UUID"},"workDate":{"type":"string","format":"date","example":"2025-05-05"},"startTime":{"type":"string","format":"date-time","example":"2025-05-05T07:00:00.000Z"},"endTime":{"type":"string","format":"date-time","example":"2025-05-05T15:30:00.000Z"},"breakMinutes":{"type":"integer","default":0,"example":30,"description":"Pausendauer in Minuten"},"activity":{"type":"string","nullable":true,"example":"Rasenfläche gemäht, Beete bepflanzt"}}},"example":{"employeeId":"uuid-employee","workDate":"2025-05-05","startTime":"2025-05-05T07:00:00.000Z","endTime":"2025-05-05T15:30:00.000Z","breakMinutes":30,"activity":"Rasenfläche gemäht"}}}},"responses":{"201":{"description":"Angelegter Zeiteintrag","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeEntry"}}}},"400":{"description":"Fehlende Pflichtfelder oder ungültige Zeiten","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Fehlender oder ungültiger API-Schlüssel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Mitarbeiter nicht gefunden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}