{"openapi":"3.0.3","info":{"title":"agrar online Partner-API","version":"1.0.0","description":"Die agrar online Partner-API ermöglicht externen Systemen (DMS, CRM, ERP etc.) den kontrollierten Zugriff auf Ihre Daten.\n\n**Auth:** X-API-Key Header. Verwaltung unter Optionen → API-Zugänge.\n\n**Mandantentrennung:** Jeder Key ist exklusiv einer Firma zugeordnet.\n\n**Rate Limit:** 10.000 Anfragen/Tag.","contact":{"name":"agrar online Support","url":"https://agrar.online"}},"servers":[{"url":"https://api.agrar.online","description":"agrar online Produktion"},{"url":"https://api.agrar.dev","description":"agrar.dev (DEV)"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API-Key aus Optionen → API-Zugänge (Format: ak_...)"}}},"security":[{"ApiKeyAuth":[]}],"paths":{"/api/v1/kunden":{"get":{"tags":["Kunden"],"summary":"Kunden abrufen","description":"Scope: kunden:read","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"suche","in":"query","schema":{"type":"string"},"description":"Freitextsuche nach Name/E-Mail"},{"name":"typ","in":"query","schema":{"type":"string","enum":["VK","EK","BEIDE"]}},{"name":"limit","in":"query","schema":{"type":"integer","default":100}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Kundenliste"},"401":{"description":"Ungültiger Key"},"403":{"description":"Fehlender Scope"}}},"post":{"tags":["Kunden"],"summary":"Kunden anlegen","description":"Scope: kunden:write — Routet via intern geroutet auf /kunden, alle Defaults (kundennr-Generierung, staat='DE', zahlungsziel_tage=30, partner_typen=['kunde']) greifen. Mit `kundennr` im Body kann eine manuelle Nummer vergeben werden — bei Duplikat in der Firma kommt 409 KUNDENNR_BEREITS_VERGEBEN.","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["nachname_firma"],"properties":{"kundennr":{"type":"string","description":"Optional. Wenn weggelassen: auto-generiert als K-{1000+count+1}. Bei Duplikat: 409."},"nachname_firma":{"type":"string","description":"Pflicht. Firmenname oder Nachname. Alias `name` wird als Fallback akzeptiert."},"name":{"type":"string","description":"Alias zu nachname_firma (Abwaertskompatibilitaet)."},"anrede":{"type":"string","enum":["Herr","Frau","Firma"]},"vorname":{"type":"string"},"strasse":{"type":"string"},"plz":{"type":"string"},"ort":{"type":"string"},"staat":{"type":"string","default":"DE"},"email":{"type":"string"},"telefon":{"type":"string"},"telefon2":{"type":"string"},"zahlungsziel_tage":{"type":"integer","default":30},"steuernummer":{"type":"string"},"ust_id":{"type":"string"},"betriebsnummer":{"type":"string"},"iban":{"type":"string"},"bic":{"type":"string"},"vb_id":{"description":"Vertriebler-ID oder -Name (gegen stammdaten typ=vertriebler aufgeloest).","oneOf":[{"type":"integer"},{"type":"string"}]},"partner_typen":{"type":"array","items":{"type":"string","enum":["kunde","lieferant"]},"default":["kunde"]},"notizen":{"type":"string"}}}}}},"responses":{"201":{"description":"Angelegt"},"409":{"description":"KUNDENNR_BEREITS_VERGEBEN"},"401":{"description":"Ungültiger Key"}}}},"/api/v1/kunden/{id}":{"get":{"tags":["Kunden"],"summary":"Kunden abrufen (Einzeln)","description":"Scope: kunden:read","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Kundendaten"},"404":{"description":"Nicht gefunden"}}},"put":{"tags":["Kunden"],"summary":"Kunden aktualisieren","description":"Scope: kunden:write","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Aktualisiert"},"404":{"description":"Nicht gefunden"}}}},"/api/v1/kunden/{id}/dokumente":{"post":{"tags":["Kunden"],"summary":"Dokument hochladen","description":"Scope: dokumente:write — Base64-codiert","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["dateiname","inhalt_base64"],"properties":{"dateiname":{"type":"string"},"inhalt_base64":{"type":"string","description":"Base64-codierter Dateiinhalt"},"beschreibung":{"type":"string"}}}}}},"responses":{"201":{"description":"Hochgeladen"},"404":{"description":"Kunde nicht gefunden"}}}},"/api/v1/artikel":{"get":{"tags":["Artikel"],"summary":"Artikel abrufen","description":"Scope: artikel:read","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"suche","in":"query","schema":{"type":"string"}},{"name":"gruppe_id","in":"query","schema":{"type":"integer"}},{"name":"limit","in":"query","schema":{"type":"integer","default":100}}],"responses":{"200":{"description":"Artikelliste"}}}},"/api/v1/artikelgruppen":{"get":{"tags":["Artikel"],"summary":"Artikelgruppen","description":"Scope: artikelgruppen:read","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Gruppensliste"}}}},"/api/v1/belege":{"get":{"tags":["Belege"],"summary":"Belege abrufen","description":"Scope: belege:read","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"typ","in":"query","schema":{"type":"string","enum":["AN","AU","LS","RE","BS","EL","ER"]}},{"name":"status","in":"query","schema":{"type":"string"}},{"name":"von","in":"query","schema":{"type":"string","format":"date"}},{"name":"bis","in":"query","schema":{"type":"string","format":"date"}},{"name":"kunden_id","in":"query","schema":{"type":"integer"}},{"name":"limit","in":"query","schema":{"type":"integer","default":50}}],"responses":{"200":{"description":"Belegliste"}}},"post":{"tags":["Belege"],"summary":"Beleg anlegen (z.B. EK-Lieferschein, VK-Lieferschein aus Jira-Push)","description":"Scope: belege:write — Routet via intern geroutet auf /belege, alle Hooks (Charge-Anlage, Lager-Buchung, Steuer-Logik) greifen automatisch.\n\n**Dashboard-Modus (Jira-Integration etc.)**: statt interner IDs koennen `kundennummer` + pro Position `artikelnr` mitgegeben werden — beide werden auf die internen IDs aufgeloest, bei unbekannten Werten kommt 400 (`KUNDE_NICHT_GEFUNDEN` / `ARTIKEL_NICHT_GEFUNDEN`).\n\n**Auto-Pricing**: wenn `ep_netto` pro Position nicht gesetzt ist, wird der Preis aus dem Artikel-Stamm gezogen — bei `staffelpreise_aktiv=true` aus der passenden Staffel (groesste `ab_menge` <= position.menge). `mwst_satz` wird genauso aus dem Stamm uebernommen wenn nicht gesetzt. Mit `auto_price: false` im Body abschaltbar.\n\n**Idempotenz**: mit `external_ref` (z.B. Jira-Issue-Key) liefert ein zweiter Aufruf den bestehenden Beleg zurueck (Antwort enthaelt `idempotent: true`).\n\nPro Position koennen optional `fremde_charge_nr`, `mhd`, `herkunftsland`, `biozertifikat_id` mitgegeben werden — bei chargenpflichtigen Artikeln wird die Charge mit diesen Infos angelegt.","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["beleg_typ","items"],"properties":{"beleg_typ":{"type":"string","enum":["AN","AU","LS","RE","BS","EL","ER"],"description":"AN=Angebot, AU=Auftrag, LS=Lieferschein, RE=Rechnung, BS=Bestellung, EL=EK-Lieferschein, ER=EK-Rechnung"},"kunden_id":{"type":"integer","description":"Beleg-Partner (Kunde oder Lieferant je nach Typ). Alternative: kundennummer."},"kundennummer":{"type":"string","description":"Statt kunden_id: Auflösung ueber die Kundennummer (kundennr). Nur eine der beiden Angaben noetig."},"external_ref":{"type":"string","description":"Externe Referenz (z.B. Jira-Issue-Key). Macht den Push idempotent — zweiter Call mit derselben Ref liefert bestehenden Beleg mit `idempotent:true` zurueck."},"auto_price":{"type":"boolean","default":true,"description":"Wenn true (default) und ep_netto pro Position fehlt: Preis aus artikel.ep_netto / artikel_staffelpreise ableiten. Auf false setzen wenn der Caller selbst preist."},"beleg_datum":{"type":"string","format":"date"},"notizen":{"type":"string"},"niederlassung_id":{"type":"integer"},"lagerhalle_id":{"type":"integer"},"fremdbelegnummer":{"type":"string","description":"Externe Beleg-Nr des Lieferanten/Partners"},"vb_id":{"description":"Vertriebler/Vertreter — entweder die interne stammdaten-ID (Integer) oder der **Name** als String. Wird gegen stammdaten WHERE typ='vertriebler' aufgeloest. Bei unbekanntem Wert wird NULL gespeichert (keine 400). Nuetzlich wenn das Dashboard nur den Namen aus dem externen System kennt.","oneOf":[{"type":"integer"},{"type":"string"}]},"items":{"type":"array","items":{"type":"object","required":["menge"],"properties":{"artikel_id":{"type":"integer","description":"Interne Artikel-ID. Alternative: artikelnr (Stammdaten-Lookup)."},"artikelnr":{"type":"string","description":"Statt artikel_id: Auflösung ueber die Artikelnummer aus dem Stamm. Bei unbekannter Nummer 400 ARTIKEL_NICHT_GEFUNDEN."},"menge":{"type":"number"},"einheit":{"type":"string"},"ep_netto":{"type":"number","description":"Optional. Wenn weggelassen + auto_price=true: aus Stamm/Staffel berechnet."},"mwst_satz":{"type":"number","default":7,"description":"Optional. Wenn weggelassen: aus artikel.mwst_satz uebernommen."},"rabatt_prozent":{"type":"number"},"bezeichnung":{"type":"string"},"bezeichnung2":{"type":"string"},"langtext":{"type":"string"},"fremde_charge_nr":{"type":"string","description":"Charge-Nr beim Lieferanten (nur EK)"},"mhd":{"type":"string","format":"date","description":"Mindesthaltbarkeit (nur EK)"},"herkunftsland":{"type":"string","description":"ISO-Code (nur EK)"},"biozertifikat_id":{"type":"integer","description":"Biozert-Verknuepfung (nur EK)"},"charge_id":{"type":"integer","description":"Bei VK: konkrete Charge auswaehlen (sonst per /chargen/verfuegbar suchen)"}}}}}}}}},"responses":{"201":{"description":"Beleg angelegt mit beleg_nummer + id"},"400":{"description":"Validierungsfehler (z.B. Charge fehlt fuer chargenpflichtigen Artikel)"}}}},"/api/v1/belege/{id}":{"get":{"tags":["Belege"],"summary":"Beleg + Positionen","description":"Scope: belege:read","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Beleg mit Positionen"},"404":{"description":"Nicht gefunden"}}},"put":{"tags":["Belege"],"summary":"Beleg aktualisieren","description":"Scope: belege:write — Body siehe POST","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Aktualisiert"},"404":{"description":"Nicht gefunden"}}}},"/api/v1/belege/{id}/pdf":{"get":{"tags":["Belege"],"summary":"Archiviertes PDF holen (DMS)","description":"Scope: belege:read — liefert das aktuellste archivierte PDF (datei_base64) eines finalisierten Belegs.","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"PDF als JSON {dateiname, mime_type, datei_base64}"},"404":{"description":"Kein archiviertes PDF — bitte zuerst im UI oeffnen oder /belege/:id/pdf-speichern aufrufen"}}}},"/api/v1/dokumente":{"get":{"tags":["Belege"],"summary":"Liste aller neuen Dokumente seit Datum (DMS)","description":"Scope: belege:read — typischer DMS-Polling-Endpunkt: liefert finalisierte Belege seit `since` mit pdf_url-Verweis.","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"since","in":"query","required":true,"schema":{"type":"string","format":"date"},"description":"Stichdatum YYYY-MM-DD"},{"name":"typen","in":"query","schema":{"type":"string"},"description":"Komma-separierte Beleg-Typen, z.B. RE,GS"},{"name":"status","in":"query","schema":{"type":"string"},"description":"Optionaler Status-Filter"}],"responses":{"200":{"description":"Liste neuer Dokumente"}}}},"/api/v1/chargen":{"get":{"tags":["Chargen"],"summary":"Chargen abrufen","description":"Scope: chargen:read — Liste aller Chargen mit aktuellem Bestand.","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"artikel_id","in":"query","schema":{"type":"integer"}},{"name":"partner_id","in":"query","schema":{"type":"integer"}},{"name":"mit_bestand","in":"query","schema":{"type":"boolean"},"description":"Wenn true, nur Chargen mit Bestand>0"},{"name":"limit","in":"query","schema":{"type":"integer","default":200}}],"responses":{"200":{"description":"Chargen-Liste mit Bestand"}}},"post":{"tags":["Chargen"],"summary":"Charge anlegen (Stand-Alone)","description":"Scope: chargen:write — fuer Daten-Migration (Anfangs-Bestaende). Fuer echte Wareneingaenge besser POST /api/v1/belege (EL/ER) verwenden — der legt die Charge inline an UND bucht den Lager-Zugang.","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["artikel_id"],"properties":{"artikel_id":{"type":"integer"},"charge_nr":{"type":"string","description":"Optional — sonst auto"},"fremde_charge_nr":{"type":"string"},"mhd":{"type":"string","format":"date"},"herkunftsland":{"type":"string","description":"ISO-Code (DE,IT,FR,...)"},"biozertifikat_id":{"type":"integer"},"eingang_menge":{"type":"number"},"eingang_ep":{"type":"number"},"einheit":{"type":"string"},"niederlassung_id":{"type":"integer"},"lagerhalle_id":{"type":"integer"},"partner_id":{"type":"integer"},"eingang_quelle":{"type":"string","enum":["beleg","produktion","manuell"],"default":"manuell"}}}}}},"responses":{"201":{"description":"Charge angelegt"},"400":{"description":"artikel_id fehlt"}}}},"/api/v1/produktionsauftraege":{"get":{"tags":["Produktion"],"summary":"PA abrufen","description":"Scope: produktion:read","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["schwebend","geplant","in_bearbeitung","erledigt","storniert"]}},{"name":"datum_von","in":"query","schema":{"type":"string","format":"date"}},{"name":"datum_bis","in":"query","schema":{"type":"string","format":"date"}},{"name":"artikel_id","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"PA-Liste"}}},"post":{"tags":["Produktion"],"summary":"PA anlegen","description":"Scope: produktion:write — Body wird an internen /produktionsauftraege weitergeleitet.","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rezeptur_id":{"type":"integer","description":"Wenn gesetzt, werden Inputs/Outputs aus Rezeptur skaliert"},"faktor":{"type":"number","default":1},"produktions_datum":{"type":"string","format":"date"},"niederlassung_id":{"type":"integer"},"lagerhalle_id":{"type":"integer"},"bezeichnung":{"type":"string"},"bemerkung":{"type":"string"},"zugewiesen_an":{"type":"integer"}}}}}},"responses":{"201":{"description":"PA angelegt"}}}},"/api/v1/produktionsauftraege/{id}":{"get":{"tags":["Produktion"],"summary":"PA Detail (mit Inputs/Outputs)","description":"Scope: produktion:read","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"PA + inputs[] + outputs[]"},"404":{"description":"Nicht gefunden"}}}},"/api/v1/annahmescheine":{"get":{"tags":["Rohwaren"],"summary":"Annahmescheine","description":"Scope: rohwaren:read","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string"}},{"name":"von","in":"query","schema":{"type":"string","format":"date"}},{"name":"bis","in":"query","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Liste"}}}},"/api/v1/kontrakte":{"get":{"tags":["Kontrakte"],"summary":"Kontrakte","description":"Scope: kontrakte:read","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Liste"}}}},"/api/v1/wiegungen":{"get":{"tags":["Waage"],"summary":"Wiegungen abrufen","description":"Scope: waage:read","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["offen","fertig","storno"]}},{"name":"von","in":"query","schema":{"type":"string","format":"date"}},{"name":"bis","in":"query","schema":{"type":"string","format":"date"}},{"name":"partner_id","in":"query","schema":{"type":"integer"}},{"name":"limit","in":"query","schema":{"type":"integer","default":100}}],"responses":{"200":{"description":"Wiegungs-Liste"}}},"post":{"tags":["Waage"],"summary":"Erstwiegung anlegen","description":"Scope: waage:write — externe Waagen-Software pusht eine Erstwiegung rein. Fields: waage_id, gewicht_erst_kg, kennzeichen, fahrer, partner_id, richtung, artikel_id, niederlassung_id, lagerhalle_id, charge_id, kontrakt_id, kontrakt_position_id, fremdwiegung, bemerkung.","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["waage_id","gewicht_erst_kg","partner_id"],"properties":{"waage_id":{"type":"integer"},"gewicht_erst_kg":{"type":"number"},"kennzeichen":{"type":"string"},"fahrer":{"type":"string"},"partner_id":{"type":"integer"},"artikel_id":{"type":"integer"},"richtung":{"type":"string","enum":["eingang","ausgang",""]},"niederlassung_id":{"type":"integer"},"lagerhalle_id":{"type":"integer"},"charge_id":{"type":"integer"},"kontrakt_id":{"type":"integer"},"kontrakt_position_id":{"type":"integer"},"fremdwiegung":{"type":"boolean"},"bemerkung":{"type":"string"}}}}}},"responses":{"201":{"description":"Erstwiegung gespeichert mit wiegeschein_nr + id"}}}},"/api/v1/wiegungen/{id}":{"get":{"tags":["Waage"],"summary":"Wiegung Detail","description":"Scope: waage:read","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Wiegung mit Partner/Artikel/Charge-Namen"},"404":{"description":"Nicht gefunden"}}}},"/api/v1/wiegungen/{id}/zweitwiegung":{"put":{"tags":["Waage"],"summary":"Zweitwiegung abschliessen","description":"Scope: waage:write — schliesst eine offene Wiegung mit Endgewicht ab. Auto-Richtung wird ermittelt aus Erst-/Zweitwiegung-Differenz wenn nicht explizit gesetzt.","security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["gewicht_zweit_kg"],"properties":{"gewicht_zweit_kg":{"type":"number"},"charge_id":{"type":"integer"},"kontrakt_id":{"type":"integer"},"kontrakt_position_id":{"type":"integer"},"niederlassung_id":{"type":"integer"},"lagerhalle_id":{"type":"integer"},"richtung":{"type":"string","enum":["eingang","ausgang"]}}}}}},"responses":{"200":{"description":"Wiegung fertig mit Netto-Gewicht und ermittelter Richtung"},"400":{"description":"Wiegung nicht im Zustand offen"}}}},"/api/v1/laender":{"get":{"tags":["Sonstiges"],"summary":"Länderliste","description":"Kein Scope erforderlich — nur gültiger API-Key","security":[{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Länder mit ISO-Codes"}}}},"/api/v1":{"get":{"tags":["Info"],"summary":"API-Info","description":"Gibt API-Version, Endpunkte und Docs-URL zurück (kein Auth nötig)","responses":{"200":{"description":"API-Info"}}}}}}