API-Änderungsprotokoll

Versionshistorie · Changelog

API Changelog

Alle Änderungen und Verbesserungen der xoPort REST API im Überblick.


Versionen

v2.122.6 Aktuell

6. Oktober 2026
Produktbewertungen exportieren
  • Neu: GET /reviews, GET /review/{reviews_id} und der Bereich GET /reviews/{von}:{bis} mit dem Scope products:read. Bisher lieferte die API am Produkt nur den Durchschnitt und die Anzahl der Bewertungen (products_rating, products_rating_count).
  • Je Bewertung: reviews_id, products_id, products_model, reviews_rating, status, promo_status, author, verified_purchase, helpful_count, review_external_id, date_added, last_modified und descriptions[] je Sprache mit reviews_title, reviews_text und reviews_reply (Antwort des Shops).
  • Filter: products_id (auch als Kommaliste), status (1 = freigegeben, Vorgabe; 0 = wartet auf Freigabe; all) und since (angelegt oder zuletzt geändert ab). Geblättert wird wie bei den Produkten mit limit und page.
  • Keine Kundendaten: author ist der Name, den auch die Produktseite zeigt. Kunden-ID, E-Mail und Bestellnummer gibt der Endpunkt nicht aus, nur verified_purchase. Ab Shop 4.9.153.

v2.122.5

1. Oktober 2026
XML-Schnittstelle: Produktexport und Lieferanten
  • Der XML-Produktexport brach mit HTTP 500 ab, sobald ein Artikel noch nicht im Schnellsuch-Index stand. Diese Artikel werden jetzt normal exportiert, der JSON-Export war nicht betroffen.
  • Der XML-Produktimport gleicht den Knoten suppliers jetzt ab, statt die Lieferanten zu löschen und neu anzulegen. Die Lieferanten-Artikelnummer bleibender Lieferanten bleibt erhalten. Ab Shop 4.9.139.

v2.122.4

30. September 2026
Merkmale und Tickets
  • Merkmale: options[].cost ist jetzt ein anderer Name für ext_cost. Ein so geschriebener Einkaufspreis landete bisher an einer Stelle, die weder Produktseite noch Checkout noch Editor lesen. Werden beide gesendet, gilt ext_cost. Einkaufspreis und UVP werden als Betrag geprüft, ungültige Werte meldet die Warnung OPTION_META_INVALID.
  • Tickets: DELETE /tickets entfernt jetzt alle am Ticket hängenden Daten mit, etwa Zuständige, Abonnenten, Termin- und Projektverknüpfungen. Ab Shop 4.9.135.

v2.122.3

30. September 2026
Bestellungen anlegen: Preise bei Bruttoberechnung
  • orders:write mit tax_flag = 1: Vom Shop ermittelte Preise (recalc) und der Versand aus compute_shipping sind Nettowerte. Sie wurden bisher als Brutto gezählt, die Steuer fehlte in der Summe. Jetzt werden sie hochgerechnet. Preise, die Sie selbst senden, bleiben wie bisher brutto.
  • Wer expected_total an die zu niedrige Summe angepasst hat, erhält jetzt die Warnung TOTAL_DRIFT (mit strict_totals HTTP 422).
  • Formelaufpreise der Standardmerkmale (apply_default_attributes) werden wie auf der Produktseite berechnet. Bisher ergab eine Formel 0 €. Formeln, die nur die Produktseite auswerten kann, melden die Warnung DEFAULT_ATTRIBUTE_FORMULA. Ab Shop 4.9.134.

v2.122.2

29. September 2026
Kunden: Adressen teilweise ändern, ohne dass das Land zurückspringt
  • PUT /Import/JSON/customers mit addresses[] und address_book_id (oder dem Objekt address) schreibt nur noch die gesendeten Felder. Bisher setzte jedes Update ohne Land das Land auf das Shopland und die Zone auf 0. Ein reines Straßen-Update machte so aus einer österreichischen Adresse eine deutsche.
  • Land und Zone ändern sich nur, wenn Sie sie mitsenden. Bei einem unbekannten Land meldet die Antwort die Warnung ADDRESS_COUNTRY. Ein Update behält dann das gespeicherte Land, eine neue Adresse erhält wie bisher das Shopland.
  • Ebenfalls behoben: Apostrophe wurden mit Backslash gespeichert, und zu lange Werte wurden ohne Hinweis byteweise gekürzt. Jetzt wird zeichenweise gekürzt, mit der Warnung ADDRESS_TRUNCATED. Ziffern in entry_country gelten nicht als Länder-ID.
  • Shops mit älterer Version: Senden Sie bei Adress-Updates immer das Land mit. Ab Shop 4.9.132.

v2.122.1

27. September 2026
Sicherheit: Kunden- und Bestellexport ohne Passwortdaten
  • Die Exporte /customers und /orders (JSON und XML) liefern den Passwort-Hash und die Token für Passwort-Reset und Kontolöschung nicht mehr aus: customers_password, customers_password_request, customers_password_request_time und customers_hash entfallen ersatzlos.
  • Der Kundendatensatz enthält außerdem kein iban und kein bic mehr. Im Bestelldatensatz bleiben beide Felder erhalten. Ab Shop 4.9.123.

v2.121.0

19. September 2026
News-Import legt fehlende Sprachen an
  • POST /Import/JSON/newsdesk ergänzt beim Anlegen jede aktive Sprache, für die kein eigener Block gesendet wurde, aus der Master-Sprache (dem de-Block, sonst dem ersten gesendeten Block). Ist die DeepL-Übersetzung eingerichtet, übersetzt sie diese Sprachen beim nächsten Lauf. Bisher blieb ein nur mit de angelegter Artikel dauerhaft einsprachig.
  • Geänderte Texte im verschachtelten languages-Format werden jetzt ebenfalls neu übersetzt. Ab Shop 4.9.106.

Die Versionsnummern 2.119.1 und 2.122.0 betreffen interne Endpunkte, 2.120.0 ist noch nicht veröffentlicht.

v2.119.0

18. September 2026
REST-API: immer mit Token, optional zusätzlich auf feste Adressen beschränkt
  • Wichtig: Die IP-Freigabe der XML-Schnittstelle (XML_PORT_IP_FILTER) berechtigt nicht mehr zur JSON/REST-API. REST verlangt immer einen OAuth2-Token, und es gelten dessen Scopes. Ein Client, der REST bisher ohne Token allein über eine freigegebene Adresse genutzt hat, erhält 401 mit reason: deny_token_required.
  • Neu ist die Einstellung XOPORT_REST_IP_LIST als Zusatzschutz. Ist sie gefüllt, muss ein Aufruf von einer gelisteten Adresse kommen und einen gültigen Token tragen, sonst folgt 403 mit reason: deny_ip_not_listed.
  • Weist die API einen Aufruf ab, nennt sie den Grund im Feld reason und im Header X-XoPort-Deny-Reason. Die XML-Schnittstelle ist unverändert. Ab Shop 4.9.83.

v2.118.1

17. September 2026
Tickets: keine falsche Warnung mehr für verknüpfte Termine
  • linked_appointments an POST/PUT /Import/JSON/tickets meldete zusätzlich die Warnung UNKNOWN_FIELD („will be ignored“), obwohl die Termine seit v2.43.0 angelegt werden. Die Warnung entfällt, an der Verarbeitung ändert sich nichts. Ab Shop 4.9.75.

v2.118.0

17. September 2026
Tickets: Bearbeitungs-Claim
  • Neuer Endpunkt PUT /Import/JSON/ticket_claims (Scope tickets:write) zeigt an, wer gerade an einem Ticket arbeitet. Pflichtfelder ticket_id, admin_id und state (claimed oder released), optional ttl_minutes (Vorgabe 60, 5 bis 240), label und force.
  • Ergebnis je Datensatz in records[].status: claimed, refreshed, taken_over, released, unchanged oder conflict. ⚠ Ein Konflikt zählt als failed.
  • GET /tickets und GET /ticket/{id} liefern die neuen Felder claim und edit_lock (das Ticket ist gerade im Backend geöffnet), jeweils null, wenn nichts vorliegt.
  • Eine öffentliche Antwort (per API oder im Backend) und der Wechsel auf einen Schließ-Status geben den Claim automatisch frei, eine interne Notiz nicht. Der Claim ist ein Hinweis, keine Sperre, und ändert weder ticket_date_last_modified noch den Verlauf. Ab Shop 4.9.73.
  • Die Versionsnummern v2.116.0 und v2.117.0 gehören zu Neuerungen, die mit dem nächsten Feature-Release erscheinen; sie werden dann hier nachgetragen.

v2.115.0

15. September 2026
XML-Importer prüfen die Feed-Datei beim Laden
  • Enthält eine Datei Zeichen, die XML 1.0 verbietet (z. B. Steuerzeichen in Beschreibungstexten), entfernen alle acht XML-Importer diese Zeichen und verarbeiten die Datei. Bisher ergab eine solche Datei 0 Datensätze, wurde trotzdem archiviert, und die Antwort meldete Erfolg.
  • Lässt sich eine Datei auch danach nicht lesen, wird sie nach xoport/xml/import/error/ verschoben statt archiviert und dort wie das Archiv nach der Aufbewahrungsfrist gelöscht.
  • Neue Antwortfelder: outcome (ok, partial, failed), files.failed und warnings.invalid_xml_chars_removed. files.processed.filenames nennt keine abgelehnten Dateien mehr.
  • ⚠ Scheitern alle Dateien eines Laufs, antwortet der Import mit "state": "error" und HTTP 422. Anbindungen, die den HTTP-Status auswerten, sehen einen solchen Ausfall damit erstmals; einzelne abgelehnte Dateien erkennen Sie an outcome: "partial". Ab Shop 4.9.65.

v2.114.0

14. September 2026
Ausgeschlossene Produkte an Kupons
  • GET /coupons und GET /coupon/{id} liefern das neue Feld restrictions.excluded_products: die Produkt-IDs, die aus dem Kupon nie Rabatt erhalten.
  • Der Ausschluss gilt zusätzlich zu allen anderen Einschränkungen und hat Vorrang vor restrictions.products. Ein ausgeschlossener Hauptartikel schließt seine Varianten ein, eine ausgeschlossene Variante nur sich selbst.
  • Kupons ohne Ausschluss liefern eine leere Liste [].

v2.113.1

14. September 2026
Kupons: restrict_mode korrigiert
  • ⚠ restrict_mode wurde bisher verkehrt herum exportiert. Richtig ist: allow_only = nur Artikel in den gelisteten Kategorien, deny = alle außer diesen, es sei denn, der Artikel liegt zusätzlich in einer nicht gelisteten Kategorie, neu deny_strict = alle außer Artikeln, die in irgendeiner gelisteten Kategorie liegen.
  • Das Feld beschreibt ausschließlich restrictions.categories. restrictions.products und restrictions.manufacturers sind immer Positivlisten; ist die Produktliste gefüllt, werden Kategorien und Hersteller nicht ausgewertet.
  • Wer das Feld seit v2.102.0 auswertet, sollte seine Zuordnung prüfen.

v2.113.0

13. September 2026
Rechnungsnummer über die Schnittstelle vergeben
  • Neues Feld assign_invoice_number an POST und PUT /Import/JSON/orders: mit true vergibt der Shop die nächste Nummer aus seinem Rechnungsnummernkreis. Die Nummer selbst ist nicht setzbar und danach unveränderlich.
  • Die Antwort meldet invoice_number_assigned, bei Erfolg zusätzlich invoice_id_individuell und invoice_date. Andernfalls nennt invoice_number_reason den Grund (mode_disabled, already_assigned, locked), begleitet von der Warnung INVOICE_NUMBER_NOT_ASSIGNED.
  • Gleichzeitige Vergaben sind gegeneinander gesperrt. Bei locked vergibt der Shop bewusst keine Nummer statt einer doppelten – senden Sie den Aufruf dann erneut.

v2.112.0

13. September 2026
Neue Kategorien mit den Vorgaben der Backend-Maske
  • ⚠ Legen Sie eine Kategorie ohne sitemap oder shop_ids an, gelten jetzt dieselben Werte wie in der Backend-Maske: sitemap = 1 und alle Shops als ausgeschriebene Liste. Bisher griffen die Spaltenvorgaben (keine Sitemap, keine Shopzuordnung).
  • Die Warnung CATEGORY_DEFAULTS_APPLIED nennt die gesetzten Werte. Aktualisierungen bestehender Kategorien sind nicht betroffen.

v2.111.0

13. September 2026
Videos und Sendungsnummern entfernen
  • videos[]: einzelne Einträge mit _action: "delete" entfernen, oder mit videos_replace: true die gesendete Liste als vollständigen Stand übernehmen (nur auf ausdrücklichen Wunsch). Die Videodatei selbst bleibt erhalten, entfernt wird die Zuordnung.
  • trackings[]: _action: "delete" über id_tracking oder service_provider und code. Eine Sendungsnummer lässt sich damit in einem Aufruf entfernen und neu anlegen.
  • Beide Wege prüfen, dass der Eintrag zum angegebenen Produkt bzw. zur Bestellung gehört.

v2.110.0

13. September 2026
Merkmale einer bestehenden Bestellposition
  • products_mode: "update" nimmt jetzt attributes entgegen. Die gesendete Liste ersetzt die Merkmale der Position vollständig, [] entfernt alle.
  • Ein abweichender Aufpreis ändert den Positionspreis nicht – die Schnittstelle warnt in diesem Fall. Soll sich der Preis ändern, stornieren Sie die Position und hängen sie neu an.
  • An bereits fakturierten Bestellungen ist die Änderung gesperrt, weil Merkmale auf Rechnung und Lieferschein stehen.

v2.109.0

13. September 2026
CSV-/XLS-Porter lesen und schalten
  • Neu: GET /Export/JSON/porters, /porter/{id} und PUT /Import/JSON/porters mit den Scopes porters:read und porters:write.
  • active setzt den Zustand (true = danach aktiv) statt ihn umzuschalten. Ohne active wird der Aufruf abgewiesen. Beim Abschalten entfernt der Shop die erzeugte Exportdatei wie im Backend.
  • Der Einzelabruf liefert unter columns die vollständige Spaltenzuordnung des Porters.

v2.108.0

13. September 2026
Einstellungen lesen und ändern
  • Neu: GET /Export/JSON/configuration und PUT /Import/JSON/configuration mit den Scopes configuration:read und configuration:write.
  • ⚠ Sichtbar sind ausschließlich Schlüssel, die der Betreiber in XOPORT_CONFIG_API_KEYS freigibt – die Vorgabe ist leer. Zugangsdaten, Lizenz- und Sitzungseinstellungen bleiben auch mit Freigabe gesperrt.
  • Geändert werden nur bestehende Schlüssel, jede Änderung wird protokolliert. Die Warnung SHOP_OVERRIDE_ACTIVE weist auf eine Domain-Überschreibung hin, die den geänderten Grundwert übersteuert.

v2.107.0

13. September 2026
Bearbeiter im Bestellverlauf
  • user_id an einem Bestell-PUT trägt den handelnden Bearbeiter in den Verlaufseintrag ein. Übernommen wird nur eine existierende Bearbeiterkennung.
  • customer_notified wird weiterhin geschrieben, die Schnittstelle meldet aber mit NO_MAIL_SENT, dass sie selbst keine Statusmail versendet.

v2.106.0

13. September 2026
Produktvideos, Optionsbilder und Unterordner hochladen
  • Media-Upload mit entity_type: "product_video" (bis 100 MB) und entity_type: "option" für Optionsbilder.
  • subfolder legt Bilder in einem Unterordner ab (eine Ebene, Kleinbuchstaben, Ziffern, _ und -).
  • Die Dateiendung wird aus dem erkannten Dateityp bestimmt, nicht aus dem gesendeten Dateinamen.

v2.105.0

13. September 2026
Der Produkt-Export lässt sich zurückschicken
  • 68 weitere Produktfelder sind schreibbar, darunter die Staffelpreise mit Mengenschwellen, die Marktplatz-Schalter, FSK 18, Sperrgut, UVP, Bonuspunkte und das Anlagedatum.
  • 14 reine Lesefelder melden sich als READONLY_FIELD, statt kommentarlos verworfen zu werden.
  • ⚠ Ungültige Werte (unbekannter Auswahlwert, zu langer Text, unlesbare Zahl) weisen den Datensatz ab, statt ihn halb zu schreiben. Leer gesendete Ja/Nein-Felder bleiben unverändert.

v2.104.0

13. September 2026
Ersetzte Produktbilder bekommen neue Vorschaubilder
  • Wird ein Bild unter demselben Dateinamen ersetzt, entfernt der Shop die zugehörigen Vorschaubilder aller Formate. Die Antwort meldet thumbnails_removed und replaced_existing.
  • Die Einträge in videos[] werden auf unbekannte Feldnamen geprüft; mit ?_strict=true führt ein Tippfehler zu HTTP 400.

v2.103.0

13. September 2026
Ticket-Abteilungen
  • Neu: GET /Export/JSON/ticket_departments (?active=), /ticket_department/{id}, POST/PUT /Import/JSON/ticket_departments und DELETE /Delete/JSON/ticket_department/{id}.
  • Abteilungen sind mehrsprachig; leere Sprachen übernehmen den Namen der Standardsprache.
  • Ist einer Abteilung noch ein Ticket zugeordnet, antwortet das Löschen mit 409 DEPARTMENT_IN_USE. ?reassign_to={id} ordnet die Tickets vorher um, ?force=1 löscht trotzdem.

v2.102.0

13. September 2026
Gutscheine und Kupons als JSON
  • Neu: GET /Export/JSON/coupons und /coupon/{id} mit den Filtern ?code=, ?type=, ?active=, ?kind= und ?valid_on=.
  • Einschränkungen kommen als ID-Listen, dazu die lesbaren Felder restrict_mode und is_credit_voucher. 0 bei den Einlösegrenzen bedeutet unbegrenzt.
  • Behoben: Name und Beschreibung eines Kupons fehlten bisher im Export.

v2.101.0

13. September 2026
Der XML-Export prüft Berechtigungen
  • ⚠ Ein OAuth2-Token mit eingeschränktem Scope erhält im XML-Export jetzt 403 für Bereiche ohne Berechtigung: customers:read, orders:read (auch für Gutscheine), products:read, categories:read, manufacturers:read.
  • Anbindungen über AppKey oder IP-Freigabe sind nicht betroffen.

v2.100.0

13. September 2026
Filterkonfiguration der Kategorieseiten
  • Neu: GET /Export/JSON/products_filters und /products_filter/{fid} mit ?options_id= und ?categories_id= – welche Filter eine Kategorieseite anbietet.
  • Hinweis: 2.100.0 folgt auf 2.99.0. Vergleichen Sie Versionen mit version_compare(), nicht als Dezimalzahl.

v2.99.0

13. September 2026
Kundenexport mit Filtern und Blättern
  • Neue Filter an /customers: customers_id, customers_group_id, language_id, email, lastname, company, customers_id_extern, die Präfixfilter email_prefix, lastname_prefix, company_prefix sowie purchased_without_account, sort, order und page.
  • Große Kundenbestände werden in der Datenbank geblättert statt vollständig geladen.
  • Ein unbrauchbarer Filterwert führt zu 400 INVALID_FILTER_VALUE, statt still ignoriert zu werden.

v2.98.0

12. September 2026
Sendungsnummern, Bewertungsanfrage, Positionsfelder
  • trackings (Mehrzahl, wie im Export) wird angenommen. Doppelte Sendungsnummern werden übersprungen und gemeldet, ein fehlender Link wird aus dem Versanddienstleister ergänzt.
  • rating_mail ist schreibbar.
  • products_mode: "update" ändert Felder bestehender Positionen (EK-Preis, Name, Artikelnummer, EAN, Einheit, Versandprofil, Datumsfelder). Menge, Preise und Steuersatz werden abgewiesen.

v2.97.0

12. September 2026
Ticket-Anhänge entfernen, Mail erneut senden
  • Neu: DELETE /Delete/JSON/ticket_attachment/{tshid}?filename=.
  • notify_history_id an PUT /Import/JSON/tickets sendet die Antwortmail zu einem bestehenden Verlaufseintrag erneut, ohne einen neuen anzulegen.
  • apply_value_defaults übernimmt beim ersten Zuordnen eines Merkmals die Vorgaben des Optionswerts (auf ausdrücklichen Wunsch, weil die Vorgaben einen Aufpreis tragen können).

v2.96.0

12. September 2026
Merkmale und Optionswerte als eigene Endpunkte
  • Neu: GET /Export/JSON/products_options, /products_option/{id}, /products_options_values sowie POST/PUT /Import/JSON/products_options.
  • values[] ist kein vollständiger Stand: entfernt wird nur, was _action: "delete" trägt, _action: "attach" teilt einen bestehenden Wert.
  • ⚠ Eine unbekannte option_id im Produktimport wird jetzt mit OPTION_NOT_FOUND abgewiesen, statt still ein Merkmal anzulegen.

v2.95.0

12. September 2026
Einzelne Verlaufseinträge löschen
  • Neu: DELETE /Delete/JSON/order_status_history/{id} und DELETE /Delete/JSON/ticket_status_history/{id}. Der Status von Bestellung bzw. Ticket wird wie in der Maske nachgeführt.
  • Der älteste Eintrag einer Bestellung ist geschützt (409 FIRST_HISTORY_ENTRY).

v2.94.0

12. September 2026
Lieferzeit-Profile
  • Neu: /shipping_profiles und /shipping_profile/{id} zum Lesen und Schreiben, mit Stufen, Lieferzeitspannen und Kundentexten je Sprache.
  • Am Produkt liefert der Export zusätzlich shipping_profile_effective, _name, _step und _text.

v2.93.0

12. September 2026
Schnellsuche und Adressierung über die Produkt-ID
  • Per Schnittstelle angelegte oder geänderte Produkte werden in die Schnellsuche aufgenommen. Der Export zeigt unter quicksearch den Indexstand je Sprache.
  • Behoben: ein PUT mit products_id konnte mit HTTP 500 enden, und Adressänderungen erzeugten auf diesem Weg keine Weiterleitung.

v2.92.0

12. September 2026
Optionsregeln des Konfigurators
  • options_rules[] am Produkt, lesend und schreibend. Fehlt das Feld, bleiben vorhandene Regeln unangetastet.
  • Aktive Regelblöcke werden vor dem Schreiben auf Vollständigkeit geprüft; ohne die zugehörige Lizenz wird abgewiesen.

v2.91.0

12. September 2026
Statusverlauf und Belegtexte je Bestellung
  • Der Bestell-Export liefert status_history[] mit allen Verlaufseinträgen.
  • indiv_text0 bis indiv_text4 (Ersatztexte je Beleg) sowie indiv_rg und indiv_ls (Dateinamen hinterlegter PDF) sind schreibbar.
  • Warnungen eines Bestell-PUT erscheinen jetzt am jeweiligen Eintrag in records[].

v2.90.0

12. September 2026
Steuerklassen und Ticket-Details
  • Neu: GET /Export/JSON/tax_classes und /tax_class/{id} mit den Steuersätzen je Steuerzone.
  • Der Ticketverlauf liefert die Message-ID eingegangener Mails und ticket_event_type.
  • Behoben: eine reine Änderung des Videotitels endete mit HTTP 500.

v2.89.0

12. September 2026
Teilerfolge werden gemeldet
  • Behoben: beim Anhängen von Positionen konnte statt des Steuertitels ein Sprachschlüssel auf der Rechnung landen.
  • Ohne gültige admin_id entsteht kein Ticket-Verlaufseintrag mehr.
  • outcome meldet partial, wenn ein Teil des Auftrags abgelehnt wurde. Neu ist außerdem ?order_id= an /tickets.

v2.88.0

12. September 2026
Produkt-Reiter mehrsprachig
  • tabs wird in der flachen, der sprachgeschachtelten und der Export-Form angenommen; der Export liefert einen languages-Block je Reiter.
  • Neue Kategorien ohne sitemap/shop_ids melden sich mit CATEGORY_DEFAULTS_APPLIED.

v2.87.0

12. September 2026
Gebühren und Bestellreferenz schreibbar
  • handling_fee, transaction_fee, selling_fee, other_fee und po_number lassen sich per POST und PUT setzen.
  • Beträge außerhalb des Wertebereichs und zu lange Referenzen werden abgewiesen statt gekürzt. Neu ist der Filter ?po_number= an /orders.

v2.86.0

12. September 2026
Blättern und Sortieren
  • ?sort= und ?order= an /orders und /products; ?page= ist umgesetzt und wird als stats.page bestätigt.
  • page zusammen mit offset ist ein Fehler. Eine gekappte limit-Angabe meldet sich mit LIMIT_CAPPED.

v2.85.0

12. September 2026
Sonderpreise vollständig im Export
  • Sonderpreise ohne Label fehlten bisher im Export. Jetzt kommen alle Felder mit, einschließlich Kundengruppe, Zeitraum, Label und Typ.
  • Neu angelegte Produkte tragen products_last_modified und erscheinen damit im Änderungsfilter.

v2.84.0

12. September 2026
Kategorie-Sperren schreibbar
  • subs_block und tree_block sind schreibbar. ⚠ subs_block wirkt auf Weiterleitungen und Sitemap des gesamten Kategorie-Astes.

v2.83.0

12. September 2026
Alle Export-Endpunkte prüfen ihre Filter
  • Unbekannte Filter werden an jedem Export-Endpunkt mit UNKNOWN_FILTER gemeldet; mit ?_strict=true bricht der Aufruf ab.

v2.82.0

12. September 2026
Hersteller über den Namen
  • manufacturers_name und der Herstellerblock des Exports werden aufgelöst. Unbekannte oder mehrdeutige Namen werden abgewiesen, nicht neu angelegt.
  • Dokumentiert, welche Daten DELETE /product mit entfernt – oft ist products_status = 0 die bessere Wahl.

v2.81.0

12. September 2026
Sonderpreise und Zusatzfelder zurückschickbar
  • special wird als Liste oder Einzelobjekt angenommen, slp_value als Label-Name.
  • extra_fields erscheint auch ohne Einträge als leere Liste.

v2.80.0

12. September 2026
Zurückschicken ohne Datenverlust
  • Behoben: Kategoriezuordnungen in der Form des Exports verschoben Produkte in Kategorie 1.
  • Bildlisten werden erst geprüft und dann geschrieben; ein Hauptbild bleibt Hauptbild.

v2.79.0

12. September 2026
Kategorien teilweise aktualisieren
  • Ein Kategorie-PUT braucht den Sprachblock nicht mehr.
  • categories_mode: "append" ergänzt Zuordnungen am Produkt; der Vollabgleich meldet entfernte Zuordnungen mit CATEGORY_ASSIGNMENTS_REPLACED.

v2.78.0

12. September 2026
Kundenbezug eines Tickets per PUT
  • ticket_customers_orders_id, ticket_customers_id, ticket_customers_name und ticket_customers_email werden beim PUT geschrieben. Die Mailadresse lässt sich nicht leeren.

v2.77.0

12. September 2026
Alles oder nichts beim Import
  • ?_strict=true weist Produkte, Kategorien, Kunden und Bestellungen mit unbekannten Feldern vollständig ab (400 UNKNOWN_FIELD), bevor etwas geschrieben wird.

v2.76.0

12. September 2026
Produkte über die ID adressieren
  • Ein PUT mit products_id genügt. Mehrfach vergebene Artikelnummern werden abgewiesen statt einen beliebigen Artikel zu treffen.

v2.75.0

12. September 2026
Exakte Produktfilter
  • Neu: products_model und products_ean als exakte Filter, products_id als Filter; manufacturers_id wirkt jetzt.
  • Liefert eine einzelne ID im Mehrzahl-Pfad mehrere Datensätze, meldet sich das mit RANGE_START_NOT_SINGLE_RECORD.

v2.74.0

12. September 2026
Tickets: kein Versand mit fehlendem Anhang
  • Wird ein Anhang abgewiesen, geht keine Kundenmail raus; der Kommentar bleibt gespeichert.
  • Neues Antwortfeld outcome (ok, partial, failed) als verlässliches Erfolgskriterium.

v2.73.0

12. September 2026
Zahlen im JSON ohne stillen Datenverlust
  • Wandelt die Zahlenerkennung Werte um (z. B. führende Nullen), meldet sich das mit NUMERIC_CHECK_APPLIED. ?numeric_check=false liefert solche Werte unverändert als Text.

v2.72.0

12. September 2026
Belegabruf ohne Nebenwirkung
  • Behoben: der Abruf einer Rechnungskorrektur über order_document konnte eine Korrekturnummer erzeugen. Ohne vorhandene Korrektur antwortet der Endpunkt jetzt mit 409.
  • Nicht angewendete Felder eines Bestell-PUT melden sich mit FIELD_IGNORED_ON_UPDATE. Zahlart, Rechnungs- und Lieferadresse sind per PUT korrigierbar.

v2.71.0

12. September 2026
Merkmale vollständig lesen und schreiben
  • ⚠ Bestehende Merkmalszuordnungen werden aktualisiert statt übersprungen; nicht gesendete Felder bleiben unverändert.
  • options_replace gleicht per Differenz ab – Optionsbilder, Grundpreisfaktor und EK gehen nicht mehr verloren. _action: "delete" entfernt eine einzelne Zuordnung.
  • Neu angelegte Merkmale melden sich mit OPTION_CREATED; options_strict_names weist unbekannte Namen stattdessen ab.

v2.70.0

12. September 2026
Löschen verlangt jetzt die Methode DELETE
  • ⚠ Bitte prüfen Sie Ihre Anbindung. Die Endpunkte unter Delete/JSON/ nehmen ab sofort ausschliesslich die HTTP-Methode DELETE an. Jede andere Methode beantwortet die Schnittstelle mit 405 METHOD_NOT_ALLOWED und dem Kopfzeilenfeld Allow: DELETE – und zwar bevor irgendetwas geschieht.
  • Warum das nötig war: Bisher prüfte die Schnittstelle die Methode an dieser Stelle gar nicht. Ein einfacher Aufruf der Adresse im Browser, eine Linkvorschau oder ein Suchmaschinen-Roboter konnte damit einen Datensatz löschen. Es genügte, dass ein Zugangstoken einmal in einer Adresse auftauchte.
  • Auswirkung in der Praxis: voraussichtlich keine. Wir haben die Zugriffsprotokolle vor der Umstellung ausgewertet – alle beobachteten Löschaufrufe verwendeten bereits DELETE. Wenn Ihr Programm hingegen GET oder POST verwendet, stellen Sie es bitte um.
Fehlermeldungen sind jetzt auswertbar
  • Unbekannte Adressen, ungültige Nummernbereiche und nicht vorhandene Endpunkte antworten jetzt mit JSON und dem richtigen HTTP-Statuscode (404, 400, 501) statt mit einer Textzeile. Bisher meldete der Server je nach Betriebsart der PHP-Umgebung den Code 200 – eine Fehlerbehandlung, die den Statuscode auswertet, hielt den Aufruf also für erfolgreich.
  • Die Fehlerobjekte folgen dem gewohnten Aufbau: error.code (NOT_FOUND, INVALID_RANGE, NOT_IMPLEMENTED) und error.message.

v2.69.0

7. September 2026
Empfänger in Kopie (CC/BCC) sind jetzt schreibbar
  • Neues Feld receivers an POST/PUT /ticket(s): { mode, cc, bcc }. Bisher liessen sich die Empfänger nur lesen – pflegen ging ausschliesslich in der Maske des Shops.
  • Die Betriebsart add (Vorgabe) ergänzt, replace ersetzt nur die mitgegebenen Listen, remove entfernt einzelne Adressen. Adressen werden wie im Backend geprüft; eine Liste, die nur ungültige Adressen enthält, lässt den Bestand unverändert – ein Tippfehler darf keine Empfängerliste leeren.
  • Der Vorgang läuft vor Kommentar und Mailversand desselben Aufrufs: „Adresse ergänzen und antworten“ geht damit in einem einzigen Aufruf.

v2.68.0

31. August 2026
Hauptkategorie je Produkt
  • Neues Feld main_category_id an der Produkt-Entität. Es bestimmt, welche der mehreren Kategoriezuordnungen den Kategoriepfad in der Adresse, die kanonische Adresse, den Eintrag in der Sitemap und die Kategorieangabe in den Preisportal-Feeds liefert.
  • ⚠ Die Import-Semantik weicht bewusst von categories ab: fehlt das Feld, bleibt der Bestand unangetastet. Andernfalls würde jedes bestehende Programm, das Produkte aktualisiert, beim ersten Lauf die Hauptkategorie löschen. Der Wert 0 hebt die Festlegung auf.
  • Eine Kategorie, die dem Produkt gar nicht zugeordnet ist, wird abgewiesen und gemeldet – ein Import darf keine ins Leere zeigende Zuordnung erzeugen.

v2.67.0

26. August 2026
Gewährleistungstyp ist jetzt importierbar
  • Neues Feld warranty_type im Produkt-Import. Zulässig sind physical und digital; die Schreibweise wird geheilt. Es gilt die gewohnte array_key_exists-Semantik: fehlt der Schlüssel, bleibt die Spalte unangetastet, ein leerer String leert sie.
  • Der Export lieferte das Feld immer schon mit – nur der Import kannte es nicht. Ein Roundtrip „exportieren, ändern, zurückschreiben“ war damit unmöglich. Dieselbe Lücke hatte v2.63.0 für die Lagerbestandsfelder geschlossen.
  • ⚠ Ein unbrauchbarer Wert wird abgelehnt, nicht zurechtgebogen. Sie erhalten eine Warnung in warnings[], der bestehende Wert bleibt stehen. Das ist Absicht: Das Feld entscheidet mit darüber, ob der Shop eine Position als Ware oder als digitalen Inhalt behandelt – und damit über die Pflichtangaben zur Gewährleistung.
Ohne API-Vertrag, aber im selben Zug behoben
  • CSV-Import: warranty_period wird jetzt gegen die zulässigen Werte geprüft. Bisher wurde ein Freitext lediglich kleingeschrieben und dann geschrieben – verändert, aber ebenso unbrauchbar. Gleiches gilt für eine ungültige Herstellergarantie, die zuvor still geleert wurde und damit eine gepflegte Angabe löschte.
  • XML-Produktimport: kennt die sechs Gewährleistungs- und Garantiefelder erstmals. Liefert Ihr Feed sie nicht mit, bleiben sie unangetastet – ein reiner Bestands- oder Preisfeed ändert daran nichts.

v2.66.1

25. August 2026
Gelöschte Termine verschwinden aus dem Ticket-Export
  • Der Ticket-Export lieferte weich gelöschte Termine weiter aus. Wer eine Löschung über das Ticket gegengeprüft hat, hielt sie für fehlgeschlagen. Betroffen war das Feld linked_appointments[] von GET /Export/JSON/tickets.
  • Ursache war eine Auslassung, kein Tippfehler. Der Block stammt aus v2.11.0 – vier Monate bevor es den Soft-Delete für Termine gab (v2.44.0) – und bekam den Filter beim Nachziehen nie. Ein Soft-Delete lässt die Verknüpfungszeile bewusst stehen (Undelete und CalDAV-Grabstein), deshalb muss jeder Leser selbst filtern. Ticketmaske und Termin-Widget im Backend taten das seit jeher – die API widersprach damit der eigenen Oberfläche desselben Shops.
  • ⚠ Verhaltensänderung ohne Anpassung auf Ihrer Seite: linked_appointments[] kann kürzer werden, und Einträge verschwinden zwischen zwei Abrufen, ohne dass sich am Ticket etwas geändert hat. An diesem Zweig gibt es keine Grabsteine.
  • Grabsteine gibt es weiterhin – und vollständiger: GET /Export/JSON/appointments?ticket_id={id}&include_deleted=1 liefert deleted_at und die Beschreibungen in allen Sprachen; für den Stapelabruf ?include_deleted=1&last_modified=… zusammen mit relationships[]. Die Tickets-Ressource bekommt bewusst kein include_deleted: der Name wäre dort mehrdeutig, weil Tickets keinen Soft-Delete kennen.
  • Gleiche Ursache im Backend mitbehoben: acht Abfragen verbanden Projekte, Bestellungen und Tickets über Termine, ohne die Termintabelle zu prüfen. Ein gelöschter Termin verband weiter, und die daraus gerenderten Termin-Links liefen ins Leere.

v2.66.0

24. August 2026
Importsperre für die Steuerklasse
  • Neuer Sperrtyp tax in import_blocks. Anlass war die Buchpreisbindung: die Preissperre allein rettet einen gebundenen Ladenpreis nicht – bleibt der Nettopreis stehen und die Steuerklasse wechselt beim Import, ändert sich der Bruttopreis trotzdem, und genau der ist der gebundene Wert. Bei preisgebundenen Waren deshalb immer zusammen mit price setzen.
  • Der Typ gilt global (ohne Sprachbezug) und wird von allen Produkt-Importern ausgewertet. Im Export erscheint er automatisch, weil import_blocks[] typ-agnostisch ist.
  • Begleitend im Backend: die Artikelmaske hat jetzt einen Reiter „Warenwirtschaft“ statt zweier Reiter, die dieselben Sperren steuerten und sich beim Speichern gegenseitig löschten.

v2.65.0

24. August 2026
Serverseitige Filter für die Produktliste
  • Bisher gab es nur ?limit und ?offset. Wer eine Teilmenge brauchte, musste den kompletten Katalog seitenweise abholen – bei 62.000 Artikeln 250 Abrufe für eine Frage, die die Datenbank in einer einzigen Abfrage beantwortet.
  • Neue Filter: ?status=, ?tax_class_id=, ?categories_id=, ?ean_prefix=, ?model_prefix= und ?last_modified=. Ein Komma trennt eine Einschlussliste (höchstens 100 Werte), mehrere Filter werden UND-verknüpft. Beispiel: ?ean_prefix=978,979,977&tax_class_id=0,1&status=1 findet alle kaufbaren Artikel mit Buch-EAN, die nicht im ermäßigten Steuersatz stehen.
  • Bewusst kein „ungleich“-Operator – den kennt die API für keine Entität. „Alles außer Steuerklasse 2“ schreibt man als Einschlussliste der übrigen Klassen; das bleibt indexfähig, statt eine zweite Abfragesprache einzuführen.
  • stats.total nennt jetzt die Treffermenge. Vorher stand dort immer die Gesamtzahl der Produkte, unabhängig von der Anfrage – stats.has_more war entsprechend falsch.
  • Ein unbrauchbarer Filterwert ist ein Fehler (HTTP 400), keine Warnung. Würde der Filter still verworfen, käme der ungefilterte Katalog zurück – eine Suche nach fehlerhaften Datensätzen läse daraus „nichts gefunden“. Filter an einer Einzelressource oder einem ID-Bereich werden aus demselben Grund abgewiesen.
  • include_inactive_products entfällt. Der Name gehörte zur Produktliste einer Kategorie, wurde am Produkt-Export nie ausgewertet und ist durch ?status= abgelöst. Er erzeugt jetzt eine Warnung statt stiller Wirkungslosigkeit.
  • Schema: neuer Index auf products_last_modified (Installer und Selbstheilung; auf sehr großen Katalogen schlägt ihn stattdessen der HealthCheck vor) – ohne ihn wäre der Delta-Filter ein vollständiger Tabellendurchlauf.

v2.64.0

24. August 2026
Bestelladressen dürfen leer bleiben
  • Die Adressspalten einer Bestellung sind jetzt NULL-fähig (Straße, Ort und Postleitzahl je Kunden-, Liefer- und Rechnungsadresse). Marktplatz-Importe in zwei Phasen legen Abholbestellungen bereits vor der Bezahlung an – die Adresse liegt dann noch gar nicht vor und wird mit dem zweiten Abruf nachgetragen.
  • Der Export liefert für solche Bestellungen null statt eines leeren Strings – null heißt „Adresse liegt noch nicht vor“, ein leerer String heißt „leer“. Firma, Adresszusatz und Bundesland waren schon immer NULL-fähig.
  • Korrektur: Die Kennzeichen für abweichende Rechnungs- und Lieferadresse vergleichen jetzt NULL-fest. Ein leeres Feld kippte sie vorher fälschlich auf „abweichend“.

v2.63.0

17. August 2026
Lagerbestände lesbar, Hauptlager-Bestandsfelder schreibbar
  • Export liefert storages[]: Der Produkt-Export gibt die Lagerbestände jetzt mit – je Lagerzeile storages_id, products_quantity, products_safe_quantity, products_reorder_level, products_storage_text und options_id. Bis v2.62.0 lieferte der JSON-Export sie gar nicht: Bestände waren per API schreib-, aber nicht lesbar, ein Roundtrip damit unmöglich. Der Feldsatz entspricht exakt dem Import, exportierte Lagerdaten sind also unverändert re-importierbar.
  • Ist das erweiterte Lagersystem deaktiviert, fehlt der Schlüssel ganz (nicht als leeres Array) – so unterscheidet ein Client „Funktion aus“ von „keine Lagerzeilen vorhanden“. Ausgegeben werden alle Lagerzeilen des Produkts, auch die zu inaktiven Lagern.
  • Hauptlager per Import setzbar: products_reorder_level und products_safe_quantity sind jetzt auf oberster Ebene des Produkts erlaubt und schreiben das Hauptlager (die products-Tabelle). Beide Spalten lieferte der Export schon immer mit, der Import verwarf sie als unbekanntes Feld – ein exportiertes Produkt ließ sich also nicht verlustfrei zurückschreiben.
  • Nicht verwechseln: Die gleichnamigen Felder innerhalb von storages[] gehören zu einem Zusatzlager. storages_id: 1 ist bereits das erste Zusatzlager – das Hauptlager ist kein Eintrag in der Lagerliste.
  • Korrektur products_quantity: Der Bestand wird beim Import nicht mehr auf ganze Zahlen gerundet. Die Spalte erlaubt Nachkommastellen, und bei aktivierter Option „Bruchmengen im Warenkorb“ sind sie fachlich gewollt (Meterware, Gewichtsartikel) – bisher wurde aus 0.5 eine 0, der Artikel also „nicht vorrätig“. Sichtbare Änderung für Anbindungen, die Nachkommastellen senden: der Wert kommt jetzt an, statt still zu verschwinden.
  • Unverändert: storages[] bleibt ein partielles Update ohne Vollspiegel (v2.45.0); nicht mitgesendete Lagerzeilen bleiben unangetastet. CSV-/XML-Import und das Speichern im Backend schreiben weiterhin die komplette Lagerzeile.

v2.62.0

13. August 2026
EU-Gewährleistungslabel (GARAN) per Schnittstelle
  • Neue Produktfelder: garan_label_enabled (0/1) gibt das EU-Gewährleistungslabel nach DVO (EU) 2025/1960 frei – verpflichtend ab 27.09.2026. Dazu garan_brand (Marke abweichend vom Katalog) und garan_model (Modellkennung fürs Label); ein leerer String löscht den jeweiligen Wert. Die Modellkennung fasst rund 14 Zeichen – längere Werte verhindern das Rendern des Labels.
  • Das Label ist rechtlich verbindlich: Jede Freigabe über die Schnittstelle wird protokolliert. Die Voraussetzungen bestätigt der Shopbetreiber, nicht die API.
  • Herstellergarantie jetzt mit halben Jahren: manufacturer_guarantee_years ist ein Dezimalwert. Werte mit Komma ("2,5") werden akzeptiert und auf die nächste niedrigere halbe Stufe gerundet, nie aufgerundet. Achtung für strenge Parser: der Export liefert jetzt "5.0" statt "5".

v2.61.0

12. August 2026
Belegdokumente einer Bestellung abrufen
  • Neuer Endpoint: GET /Export/JSON/order_document/{orders_id}?type=rg liefert den fertigen Beleg als JSON mit base64-kodiertem PDF. Verfügbare Belegtypen: of (Angebot), ab (Auftragsbestätigung), rg/rgqr (Rechnung, wahlweise mit Schweizer QR-Zahlteil), ls (Lieferschein), st (Storno), pl (Packliste), prg (Proformarechnung), dr (Spendenbescheinigung) und so. Scope orders:read.
  • Warum base64 statt Binärstrom: Das Feld heißt content_base64 – genau wie bei den Ticket-Anhängen aus v2.60.0. Ein Beleg lässt sich damit ohne Zwischendatei direkt an ein Ticket hängen.
  • Erzeugt wird genau der Beleg des Backends: Es läuft derselbe Renderer wie für die Mailanhänge, in einem eigenen Prozess und ohne Sitzung. Liegt zu einer Bestellung eine hinterlegte Rechnungsdatei vor, wird diese ausgeliefert.
  • Klare Fehlerfälle: unbekannte Bestellung → 404, Rechnungsbeleg ohne Rechnungsdatum → 409, unzulässiger Belegtyp → 400 – jeweils als sauberes JSON.
  • Nicht enthalten: Die eiv_*-Typen (XRechnung, ZUGFeRD) bleiben bewusst außen vor – sie erzeugen XML statt eines PDF.

v2.60.0

11. August 2026
Ticket-Anhänge jetzt auch schreibbar
  • Neues Feld attachments[]: POST und PUT /Import/JSON/tickets nehmen jetzt Dateien entgegen – je Eintrag ein name (Dateiname inklusive Endung) und content_base64 (Inhalt Base64-kodiert).
  • Bisheriges Verhalten: Anhänge waren ausschließlich lesbar (auflisten und herunterladen seit v2.9.0); geschrieben werden konnten sie nur über das Backend-Formular. Ein per API angelegtes Ticket blieb damit ohne Beleg – PDF, .eml oder Screenshot mussten von Hand nachgereicht werden.
  • Anhänge hängen an einem Verlaufseintrag, nicht am Ticket: ohne ticket_comments im selben Request entsteht kein Eintrag, an dem die Datei hängen könnte. Sie wird dann mit einer Fehlermeldung abgelehnt statt still verworfen.
  • Gleiche Regeln wie im Backend: identische Endungs-Allowlist, dieselbe Namenskonvention und Hash-Dedup gegen Doppelablage. Der tatsächlich vergebene Dateiname steht je Datensatz in der Antwort unter attachments. Die Obergrenze je Datei entspricht dem /media-Endpunkt (Standard 10 MB).
  • Fehlertoleranz: unerlaubte Endung, ungültiges Base64 oder Überschreitung der Größe erzeugen einen Fehler je Datei – das Ticket-Update selbst bleibt erfolgreich. Bei einer öffentlichen Antwort mit Benachrichtigung gehen die Dateien mit der Kundenmail hinaus.
  • Bestehende Integrationen: bleiben ohne Anpassung lauffähig – das Feld ist optional, die Erweiterung rein additiv. Scope tickets:write.
Dokumentationskorrekturen
  • Kommentarfelder richtiggestellt: Die Endpoint-Seite nannte beim Anlegen eines Tickets die Felder initial_comment und initial_comment_admin_id – beide existieren in der Schnittstelle nicht. Richtig sind ticket_comments (Kommentartext) und admin_id. Auch beim Aktualisieren ist ticket_comments ein Text, kein Array aus Objekten. Wer sich auf die alte Beschreibung gestützt hat, bekam still keinen Kommentar – und kann folglich auch keine Anhänge ablegen, weil ohne Verlaufseintrag nichts angehängt werden kann.
  • Öffentliche Antworten: Der Hinweis „Direkte Kundenantworten sind über die API nicht möglich“ war seit v2.25.0 überholt und wurde ersetzt. Die Flags is_public_reply, notify_customer und append_signature sind jetzt in der Feldtabelle beschrieben.
  • Keine Codeänderung: Diese beiden Punkte betreffen ausschließlich die Dokumentation – das Verhalten der Schnittstelle ist unverändert.

v2.59.0

6. August 2026
Interne Erweiterungen
  • Keine Änderung an den dokumentierten Endpunkten: Diese Version erweitert ausschließlich eine interne Support-Schnittstelle von XONIC. Export, Import und DELETE verhalten sich unverändert.
  • Bestehende Integrationen: bleiben ohne Anpassung lauffähig – die Versionsnummer steigt lediglich mit.

v2.58.0

6. August 2026
Kombinierte Merkmalsgruppen im Produkt-Export
  • Neues Feld option_groups[]: Merkmale, die im Shop zu einer Merkmalsgruppe gekoppelt sind (typisch „Farbe + Größe“), liefert der Produkt-Export jetzt mit – inklusive der Gruppenmitglieder in Anzeigereihenfolge und je Kombination der zugehörigen Artikelnummer, EAN, Menge, Preis und Sortierung.
  • Bisheriges Verhalten: Gruppierte Merkmale fehlten im Export vollständig – ohne leeres Feld und ohne Hinweis. Nur ungruppierte Merkmale (options{}) wurden ausgegeben.
  • Warum zwei Felder: Bei einer Merkmalsgruppe gehören Artikelnummer und EAN zur Kombination der Werte („schwarz / L“ = eine EAN), bei einem einzelnen Merkmal zum Einzelwert. options{} bleibt daher unverändert – rein additive Erweiterung.
  • Unverändert: XML- und CSV-Porter geben weiterhin nur ungruppierte Merkmale aus. Der Import erwartet weiterhin das flache options[]-Format.

v2.57.1

22. Juli 2026
Bugfix: Kundenindividuelle Preise im XML-Produktimport
  • XML-Produktimport: Der customers-Block innerhalb von groups (kundenindividuelle Preise je Produkt) wurde beim Einlesen verworfen und kam nie in der Datenbank an – die Einträge werden jetzt korrekt importiert. Die Kundenzuordnung funktioniert per id (Shop-Kunden-ID) oder external (WaWi-Kundennummer); unbekannte Kundennummern werden übersprungen.
  • Klare Regeln für unvollständige Einträge: price 0 ohne Staffelpreise legt keine Zeile an (Entfernen = Eintrag weglassen – der Import spiegelt den Kundenpreis-Bestand je Produkt voll); fehlende quantity_blocks/min_quantity werden wie beim REST-Import mit 1 vorbelegt.

v2.57.0

18. Juli 2026
Kundenrabatte & kundenindividuelle Preise
  • Customers: Neues Feld discounts[] – Kundenrabatte (das Feld „Rabatte“ der Kundenverwaltung) per API pflegen. Semantik „replace-on-present“: Ist das Feld mit Einträgen vorhanden, wird der Rabatt-Bestand des Kunden komplett ersetzt; ein leeres [] entfernt alle Rabatte; fehlt das Feld, bleiben die Rabatte unangetastet. Der Customers-Export liefert discounts jetzt mit (Round-Trip).
  • Products-Import: Neues Feld customers_prices[] – kundenindividuelle Preise inkl. Staffelpreisen als Partial Upsert (nur mitgesendete Spalten werden geschrieben). _action: "delete" entfernt den Eintrag eines Kunden, customers_prices_replace: true schaltet auf Vollspiegel; der Kunde wird per customers_id oder customers_id_extern (WaWi-Kundennummer) adressiert.
  • Customers-Import: customers_id_extern und customers_group_id sind jetzt schreibbar; PUT kann den Kunden auch ohne E-Mail über customers_id oder customers_id_extern adressieren (reine Rabatt-/Stammdatenpflege).

v2.54.0

14. Juli 2026

Bestellungen: Die Datumsfelder – allen voran das Leistungsdatum – lassen sich jetzt auch je Position setzen. Das wird für Sammelrechnungen gebraucht, bei denen jedes Arbeitspaket ein eigenes Leistungsdatum trägt (die Belege zeigen es dann unter der jeweiligen Artikelzeile statt einmal im Belegkopf). Außerdem können per PUT /orders mit products_mode: "append" Positionen an eine bestehende Bestellung angehängt werden; die Summen werden anschließend neu berechnet. Bestellungen, die bereits als Rechnung gelten – weil eine Rechnungsnummer vergeben wurde oder der individuelle Nummernkreis deaktiviert ist – werden dabei mit ORDER_ALREADY_INVOICED abgelehnt: Eine ausgestellte Rechnung darf nachträglich nicht verändert werden. Status-, Tracking- und Datumsaktualisierungen bleiben davon unberührt.

v2.53.0

14. Juli 2026

Bestellungen: Die Datumsgruppe des Bestelleditors ist jetzt per API setzbar – service_date (Leistungsdatum), estimated_service_date, shipping_date, estimated_shipping_date, invoice_date (Rechnungsdatum), invoice_payment_term_date (Zahlungsziel) sowie der Wunschtermin desired_delivery_date als Freitext. Gilt für Neuanlage (POST) und Aktualisierung (PUT); Datumsangaben werden tolerant geparst.

v2.52.0

14. Juli 2026

Produktimport: Mit import_blocks lassen sich einzelne Felder eines bestehenden Produkts gezielt vor dem XML-Importer schützen – etwa wenn die deutsche Beschreibung aus der Warenwirtschaft kommt, die englische aber im Shop gepflegt wird. Das programmatische Gegenstück zum Backend-Tab „xoPort Datenschnittstelle (XML)". Geschützt werden können Name und Beschreibung (sprachbezogen) sowie sieben Preiskategorien.

v2.51.0

11. Juli 2026

Produktimport: extra_fields akzeptiert Zusatzfelder jetzt wahlweise über die Feld-ID oder den Feldnamen – als Objekt oder als Liste, inklusive der Struktur, die der JSON-Export liefert (Export/Import-Round-Trip). Zuvor wurde ausschließlich die Liste mit products_extra_fields_id ausgewertet; abweichende Schreibweisen wurden ohne Hinweis verworfen. Nicht auflösbare Zusatzfelder erzeugen jetzt eine EXTRA_FIELD-Warnung, und ein Feld, das fälschlich auf oberster Ebene statt in extra_fields steht, wird in der Warnung samt passender Feld-ID benannt. Betrifft u. a. die Google-Shopping-Felder.

v2.50.0

30. Juni 2026

Interne Optimierungen und Stabilitätsverbesserungen der xoPort-API.

v2.49.0

25. Juni 2026

DELETE /customer_group/{id} (Scope customer_groups:delete): Löschen einer Kundengruppe mit zwei harten Schutzmechanismen – Gruppe 0 („Endkunden“) ist nicht löschbar (403), und eine Gruppe mit noch zugewiesenen Kunden wird abgelehnt (409, erst umgruppieren). Bereinigt alle abhängigen Daten (Rabatte, Specials, Gruppenpreise, Gating-Einträge).

v2.48.0

25. Juni 2026

Neue Entität customer_groups (Export + Import): Kundengruppen per API enumerieren und verwalten – inkl. geparster restricted_categories_ids (Kategorie-Sperrliste der Gruppe). POST/PUT mit Enum-Validierung und ID-Härtung; POST verlangt eine explizite customers_group_id (kein AUTO_INCREMENT). Zwei neue Scopes customer_groups:read + customer_groups:write.

v2.47.0

25. Juni 2026

Kategorie-Kundengruppen-Sperrliste: restricted_customer_groups jetzt auch für Kategorien (zuvor nur Produkte). Import synchronisiert customers_groups.restricted_categories, Export liefert das Feld zurück. Rekursiv: Sperrt man eine Eltern-Kategorie, werden alle aktiven Unterkategorien mitgesperrt.

v2.46.0

20. Juni 2026

Media-Upload entity_type=static: neuer Wildcard-/Denylist-Modus. Mit XOPORT_MEDIA_STATIC_FOLDERS = '*' sind alle images/-Unterordner beschreibbar – außer den vom Bild-System verwalteten (source/, thumbnail/). Der Standard bleibt die strikte Freigabeliste; der Modus ist optional. Zusätzliche Härtung: strenger Ordnername-Zeichensatz, Pfad-Confinement unter images/ und ein Überschreibschutz (vorhandene Datei nur mit overwrite:true). Neue Fehlercodes PROTECTED_TARGET_FOLDER (403) und FILE_EXISTS (409).

v2.45.0

19. Juni 2026

Produkt-Import storages[]: partielles Update. Pro-Lager-Bestände werden nur noch für die tatsächlich übergebenen Felder geschrieben (INSERT … ON DUPLICATE KEY UPDATE statt REPLACE). Ein Teil-Payload wie {"storages_id":1,"products_reorder_level":50} ändert damit nur den Meldebestand und setzt die übrigen Lagerspalten nicht mehr auf 0. Voll-Payloads bleiben abwärtskompatibel.

v2.44.0

18. Juni 2026
Appointments API: eigenständiger Kalender-Endpoint
  • Neue Top-Level-Ressource appointments mit Full-CRUD: GET /appointments, POST/PUT /appointments sowie DELETE /appointment/{id} (Soft-Delete → CalDAV-Tombstone).
  • Termine werden über die zentrale Termin-Logik angelegt (CalDAV-UID, updated_at) und erscheinen sofort im Kalender der zuständigen Admins sowie in der CalDAV-Synchronisation.
  • Felder u. a.: startdate (Pflicht), enddate/deadline, allday/vacation/private/done, Erinnerungen, Titel/Beschreibung/Ort (mehrsprachig), responsibilities, followers, categories und relationships (Verknüpfung zu Ticket, Bestellung, Kunde oder Projekt).
  • Export-Filter: Zeitfenster, Zuständigkeit, Verknüpfung (z. B. ?ticket_id=) sowie ?last_modified= für Delta-Sync. PUT erhält nicht übergebene Felder.
  • Drei neue Scopes: appointments:read, appointments:write, appointments:delete.

v2.43.0

18. Juni 2026
Tickets API: verknüpfte Termine anlegen
  • POST/PUT /tickets akzeptiert jetzt das optionale Feld linked_appointments — pro Ticket lassen sich Termine direkt mit anlegen.
  • Jeder Eintrag erzeugt einen Kalender-Termin und verknüpft ihn mit dem Ticket. Zuständige = die dem Ticket zugewiesenen Admins, Titel-Fallback = Ticket-Betreff.
  • Pendant zum bereits vorhandenen Export-Feld linked_appointments; benötigter Scope: tickets:write.

v2.42.0

15. Juni 2026
Slider Entity-Types & News-Slider (ab Shop 4.8)
  • Die Tabelle slider nutzt ab XONIC 4.8 (alles nach Release 4.7.15) das polymorphe Spaltenpaar entity_type (category / content / news) + entity_id statt categories_id + content_id.
  • Das Slider-Payload (Export & Import) führt entsprechend entity_type + entity_id. Clients, die noch categories_id/content_id senden, müssen umstellen.
  • Neuer Typ news: Slides lassen sich jetzt auch an News-Artikel hängen. Startseiten-Slider = entity_type=content, entity_id=0.
  • Export-Filter ?categories_id= / ?content_id= bleiben erhalten und mappen intern auf den Entity-Typ. Bestehende Shops migrieren automatisch per Self-Heal.

v2.41.0

14. Juni 2026
Media API: Projekt- & Aufgaben-Anhänge
  • POST /media mit entity_type=project (bzw. task) hängt PDF-Dokumente direkt an ein xoCRM-Projekt bzw. eine Aufgabe an.
  • Die Datei landet im internen, zugriffsgeschützten Dateibereich des Projekts und erscheint sofort im Tab „Dateien“ — ideal für interne Dokumente wie Projektpläne.
  • Pflichtfelder: entity_id (Projekt- bzw. Aufgaben-ID, wird geprüft), file_name und eine Quelle (file_url, file_base64 oder Multipart-Upload).
  • Aktuell werden PDF-Dateien (application/pdf) unterstützt; benötigter Scope: media:write.

v2.40.0

11. Juni 2026
Projects API: Statuseintrag ohne Statuswechsel
  • PUT /projects bzw. /project_tasks mit status_comment schreibt jetzt auch ohne Statusänderung einen Eintrag in den Statusverlauf — ideal für Meeting-Protokolle und Arbeitsnotizen.
  • Aufgaben-Einträge tragen den progress_percent mit — der Fortschrittsbalken erscheint im Verlauf.

v2.39.2

11. Juni 2026
Bugfix: Einmalige Optionskosten im Frontend
  • Beim Options-Import mit Vorauswahl wird jetzt auch pre_option in der Optionsmaske gesetzt — vorher erschien die „zzgl.“-Zeile für einmalige Optionskosten auf der Produktseite nicht. pre_option ist zusätzlich als explizites Feld im Payload erlaubt.
  • Core-Fix: Optionspreis ging verloren, wenn die Options-Detail-Tabelle komplett leer war.

v2.39.1

11. Juni 2026
Bugfix: Produkt-Caches nach Import
  • Nach jedem Produkt-Import werden die Produkt-Caches aktualisiert (Options-Vorauswahl, Preis-Labels) — wie beim Speichern im Backend.
  • Vorher waren per API gesetzte Merkmals-Aufpreise und Preisänderungen im Frontend erst nach einem manuellen Nachspeichern sichtbar.

v2.39.0

11. Juni 2026
Produkt-Import: Merkmale & Optionen (options[])
  • Produkt-Attribute lassen sich jetzt per POST/PUT /Import/JSON/products anlegen: pro Eintrag option_id oder option_name + value_id oder value_name — fehlende Optionen/Werte werden automatisch angelegt.
  • Steuerfelder: option_type (z. B. 5 = nur Anzeige), option_sort, option_required, price (Aufpreis), pre_select, status, model, ean, quantity, weight, image.
  • Bereits zugewiesene Kombinationen werden übersprungen (Warning OPTION_EXISTS_SKIPPED); options_replace: true baut die Attribute des Produkts vollständig neu auf.

v2.38.0

11. Juni 2026
Produkte: Kundengruppen-Sperrliste
  • Neues Feld restricted_customer_groups[] im Produkt-Export und -Import: Gruppen-IDs, für die das Produkt gesperrt ist.
  • Import ersetzt die Sperrliste vollständig, wenn das Feld im Payload enthalten ist; ein leeres Array entfernt alle Sperren.
  • Damit lassen sich Sichtbarkeits-Einschränkungen (z. B. nur für eine bestimmte Kundengruppe) per API von einem Produkt auf andere übertragen.

v2.37.0

11. Juni 2026
Produkt-Import: Click2Call & Direktkauf-Sperre
  • Zwei neue importierbare Produkt-Flags: click2call (Anruf-Button statt Warenkorb) und non_directcart (kein Direktkauf) — per POST/PUT /Import/JSON/products.
  • Verhalten wie non_cart/non_price: nur gesetzte Felder werden geändert.

v2.36.0

11. Juni 2026
Projects API: Statuseinträge im Export
  • Projekte und Aufgaben liefern jetzt status_history[] mit — alle im Backend gepflegten Statuseinträge inkl. Kommentar (z. B. Meeting-Protokolle).
  • Felder pro Eintrag: status_id + status_name, date_added, comments, progress_percent, user_id + user_name.
  • Rein additiv, kein neuer Scope — projects:read bzw. project_tasks:read genügt.

v2.35.0

11. Juni 2026
Projects API: xoCRM-Projekte & Projektaufgaben
  • Zwei neue Entity-Typen: projects (xoCRM-Projekte) und project_tasks (Projektaufgaben) — jeweils Export, Import und Delete.
  • Endpoints: GET /Export/JSON/projects (Einzel/Range/Filter, optional ?include_tasks=1 mit eingebetteten Aufgaben), POST/PUT /Import/JSON/projects, DELETE /Delete/JSON/project/{id} — analog für project_tasks.
  • Sechs neue Scopes: projects:read/write/delete + project_tasks:read/write/delete.
  • Projekte: mehrsprachige descriptions[], Kategorien, Kunden-Zuordnung (customer_scope), Projektleiter (leader_admin_ids), Verknüpfungen zu Bestellungen/Tickets (relationships[]), is_private.
  • Aufgaben: Priorität (1-5), Fortschritt (0-100 %), Fälligkeit, geplante/tatsächliche Stunden, Bearbeiter-Zuweisung (assigned_admin_ids).
  • Status-Handling: Validierung gegen die Status-Verwaltung, automatischer Status-History-Eintrag bei Anlage und Statuswechsel (status_comment, admin_id).
  • Delete-Kaskade: Projekt-Löschung entfernt auch alle zugehörigen Aufgaben; verknüpfte Bestellungen/Tickets werden nur entkoppelt.

v2.34.0

10. Juni 2026
Produkt-Import: B-Ware-Automation & neue Felder
  • warranty_period: Garantiezeitraum setzbar; ein leerer String "" überschreibt den Standard 24m.
  • products_xoport: Marker-Feld (z. B. googleblacklist) jetzt importierbar.
  • images[]: Zusätzliche Produktbilder per Dateiname zuweisen (ersetzt die bestehende Galerie), inkl. mehrsprachiger Alt-Texte; Array- oder Objektformat.
  • storages[]: Pro-Lager-Bestände (Mehrlager) setzen — nur bei aktivem Mehrlager-System; der Gesamtbestand products_quantity bleibt separat.
  • downloads[]: Produkt-Downloads (z. B. PDF) anlegen; d_src verweist auf eine Datei in files/.
  • Media-Upload: POST /media mit entity_type=download lädt PDF-/Dokumentdateien nach files/ hoch.

v2.33.0

9. Juni 2026
News-Autoren: Endpoint newsdesk_authors & author_id
  • Neuer Endpoint /newsdesk_authors: News-Autoren als eigene Ressource exportieren (GET /newsdesk_authors, GET /newsdesk_author/{id}) und schreiben (POST/PUT /newsdesk_authors). Scope news:read/news:write.
  • Felder: sprachneutral authors_name, authors_image, authors_email, authors_url, authors_company (Firma-Überschreibung für externe Autoren), authors_since_year, authors_status, sort_order; pro Sprache authors_jobtitle und authors_bio.
  • Verknüpfung author_id: Der News-Im-/Export (newsdesks) enthält jetzt author_id — Artikel lassen sich direkt mit einem Autor verbinden.

v2.32.0

7. Juni 2026
Orders Import: Checkout-Parität bei Steuerzeile & Adresszusatz
  • Steuerzeile pro Satz: ot_tax wird jetzt je Steuersatz mit rate-genauem Titel im Frontend-Format geschrieben — „zzgl. USt. 19 %“ im Netto- bzw. „inkl. USt. 19 %“ im Bruttomodus (nur wenn MODULE_ORDER_TOTAL_PREFIX=true) — statt der bisherigen statischen Sammelzeile „USt.:“. Die Beträge bleiben unverändert.
  • Neues Adressfeld additional_address: jetzt in den erlaubten Adressfeldern — der Adresszusatz wird nach customers_additional_address, delivery_additional_address und billing_additional_address übernommen (kein UNKNOWN_FIELD-Warning mehr).

v2.31.0

7. Juni 2026
Orders Import recalc: Kategorie- & Kundenrabatt im Preis
  • Der recalc-Modus wendet jetzt zusätzlich zu Gruppenpreis und Specials auch den Kategorie-/Kundenrabatt (customers_discount + customers_groups_discount, inkl. Subkategorie-Vererbung — dieselbe Logik wie das Frontend) auf den Positionspreis an.
  • Der reguläre Preis wird immer rabattiert, das Special nur bei d_special=1; der günstigere der beiden effektiven Preise gewinnt. Damit trägt die übertragene Bestellung exakt den Preis, den der Kunde auch im Checkout zahlt.
  • Best-effort: Bei Problemen wird die Warnung CATEGORY_DISCOUNT_UNAVAILABLE ausgegeben und ohne Rabatt gerechnet. Annahme: ein Kunde pro Request. Gäste-/Marktplatz-Bestellungen (ohne customers_id) bleiben unverändert.

v2.30.0

7. Juni 2026
Orders Import: Dropship-Parität zum Checkout
  • Show-only-Merkmale: apply_default_attributes übernimmt jetzt auch Anzeige-Merkmale mit Vorauswahl (Options-Typ 5, z. B. „Ausführung“/„Schutzart“) inkl. signiertem Aufpreis — exakt wie sie der Checkout mitführt. Nur interne Hidden-Merkmale (Typ 6) bleiben ausgeschlossen.
  • EK & GTIN: Im recalc-Modus werden Einkaufspreis (products_ek_price aus products_cost) und GTIN/EAN (products_ean) in die Bestellposition nachgeladen, sofern der Aufrufer nichts mitgibt.
  • Neues Flag compute_shipping (Top-Level, Default false): Ohne explizite totals.shipping wird der günstigste Versand „headless“ über den Frontend-Versand-Stack (shipping::cheapest()) für die Lieferadresse berechnet und als ot_shipping geschrieben (Steuersatz = dominanter Produktsteuersatz). Best-effort: Fehler werfen nicht, sondern liefern die Warnung SHIPPING_AUTO_FAILED bzw. SHIPPING_AUTO_EMPTY — die Bestellung bleibt erhalten.
  • Use-Case: Dropship-Übertragung (z. B. SPV → SST), bei der die Folgebestellung Merkmale, EK/GTIN und Versandkosten exakt wie eine manuelle Shop-Bestellung tragen soll.

v2.29.0

7. Juni 2026
Orders Import: optionaler Versand der Bestellbestätigung
  • Neues Flag send_confirmation_email (Top-Level, true): Nach erfolgreichem Anlegen der Bestellung wird die Standard-Bestellbestätigung an den Besteller versendet — exakt wie beim normalen Checkout bzw. den Marketplace-Importern, inkl. der Kopie-Empfänger aus SEND_EXTRA_ORDER_EMAILS_TO.
  • Empfänger & Sprache: E-Mail aus der angelegten Bestellung (mit Validierung), Sprache aus dem Payload bzw. der Bestellung.
  • Rückmeldung: Das Ergebnis steht als confirmation_email_sent (true/false) im jeweiligen Datensatz der Antwort. Mailfehler brechen den Import nicht ab.
  • Default unverändert: Ohne das Flag wird keine Mail versendet (vollständig abwärtskompatibel).

v2.28.0

7. Juni 2026
Orders Import: Kunden-Auto-Befüllung, SKU-Auflösung & Auto-Default-Attribute
  • Rechnungsadresse aus Kundennummer: Bei customers_id > 0 und unvollständigem/fehlendem billing wird die Standard-Rechnungsadresse des Kunden (Kundenstamm + Adressbuch) übernommen — explizit gelieferte Felder gewinnen. delivery bleibt für abweichende Lieferadressen eigenständig.
  • SKU-Auflösung: Positionen können allein über products_model (= SKU) + products_quantity geliefert werden; products_id und products_name werden aus dem Shop ergänzt. Unbekannte SKU → Warnung PRODUCT_MODEL_NOT_FOUND.
  • Auto-Default-Attribute: apply_default_attributes: true übernimmt je Position ohne eigene Attribute die im Shop vorausgewählten Optionswerte inkl. Aufpreis in die Bestellung.
  • Korrektur Mengen-Berechnung: Im recalc-Modus wird der Netto-Stückpreis nun korrekt gespeichert (zuvor bei Menge > 1 in der Zwischensumme verdoppelt).

v2.27.1

2. Juni 2026
Tickets: Öffentliche Antwort via API deckungsgleich zum Backend-Tool
  • Autor-Persona: Bei is_public_reply=true erscheint der Verlaufseintrag unter der Ticket-Persona des handelnden Admins (Quelle admin.ticket_settings) statt unter dem Login-Vor-/Nachnamen. Reihenfolge: optionales ticket_admin_id → admin.ticket_settings → TICKET_DEFAULT_ADMIN_ID → Login-Name.
  • „Antwort eingebunden“: Das Flag ticket_customer_notified_embedded wird bei öffentlicher Antwort mit Mailversand gesetzt — der Antworttext ist in der Kunden-Mail eingebunden.
  • CC/BCC: Die CC/BCC-Empfänger des Tickets werden am Verlaufseintrag hinterlegt (Badge sichtbar) und in der Mail mitversendet.
  • Mailversand-Prüfung: Der Rückgabewert des Mailversands wird ausgewertet — ein Fehlversand liefert jetzt einen Error statt eines stillen success.

v2.26.0

1. Juni 2026
Orders Export: SQL-Level-Pagination (Memory-Fix)
  • Problem: GET /Export/JSON/orders baute bisher den kompletten Bestell-Datensatz (inkl. Positionen, Totals, Tracking) im Arbeitsspeicher auf und schnitt erst danach via ?limit/?offset — auf Shops mit vielen Bestellungen führte das zu einem PHP-Fatal „Allowed memory size exhausted“, selbst bei ?limit=1.
  • Fix: LIMIT/OFFSET werden jetzt — analog zum /products-Endpoint — direkt auf die orders_id-Query angewandt. Es wird nur noch die angeforderte Seite geladen (Default 50, Max 250 via XOPORT_API_MAX_LIMIT).
  • Filter-aware: stats.total respektiert aktive Filter (?status, ?customer_id, ?date_from/to, ?since, ?products_id, …); die Response trägt stats.total/limit/offset/has_more.
  • Unverändert: Einzel-Ressource /order/{id} und Range /orders/{a}:{b} bleiben bewusst ohne Paging (wie bei products).

v2.25.0

30. Mai 2026
Tickets: Admin-Signatur an öffentliche Antworten
  • Automatische Signatur: Bei is_public_reply: true hängt der PUT /Import/JSON/tickets jetzt die im Backend hinterlegte Ticket-Signatur des Admins (admin_settings.signatures.tickets) automatisch an den Antworttext an — sowohl im Verlaufseintrag als auch in der Kunden-E-Mail, genau wie das Backend-Antwort-Tool.
  • Sprache: Standardsprache des Shops, Fallback auf die erste hinterlegte Signatur. Hat der Admin keine Signatur, bleibt der Text unverändert.
  • Opt-out: optionales Flag append_signature: false (strikt boolean) unterdrückt das Anhängen.
  • Wichtig für Clients: in ticket_comments keine eigene Signatur mehr mitschicken — sonst erscheint sie doppelt.

v2.24.0

29. Mai 2026
Tickets: Öffentliche Antworten + Kunden-Notify
  • Neu im Comment-Block des PUT /Import/JSON/tickets: zwei optionale boolean-Flags pro Item — is_public_reply (true → History-Eintrag wird öffentlich) und notify_customer (true → E-Mail an Kunden). Beide nur strikt === true wirksam, alle anderen Werte werden ignoriert.
  • Default-Verhalten unverändert: ohne Flags bleibt der Kommentar intern (ticket_internal_comment=1, keine Mail) — backward compatible mit allen v2.11.0+ Clients.
  • Mail-Versand nutzt das gleiche $mail_ticket_update_with_content-Template wie das Backend-Tool (xpanel/tools-ticket-show.php), inkl. CC/BCC aus ticket_additional_data.receivers und Anhängen der jeweiligen History-Zeile.
  • Sicherheits-Default: is_public_reply=true ohne gültige admin_id > 0 fällt automatisch auf intern + ohne Mail zurück und gibt einen error zurück. Kein neuer Scope nötig (tickets:write reicht).
  • Use-Case: KI-Agenten oder externe Tools können jetzt Tickets nicht nur als interne Notiz, sondern als echte Kundenantwort beantworten — mit klarem Opt-in-Flag, damit „aus Versehen öffentlich" praktisch unmöglich ist.

v2.23.0

29. Mai 2026
Products Single-Resource-Fix + API-Konsistenz
  • Bugfix: GET /products/{id} liefert jetzt genau ein Produkt. Zuvor griff wegen des Pluralpfad-Routings die Range-Semantik WHERE products_id >= {id} — der Client sah per .data[0] ein zufälliges Produkt aus dem Scan-Result.
  • BREAKING CHANGE für Categories: Das in v2.1.0 eingeführte ?id=-Query-Form bei /categories?id={id} wurde entfernt. Single-Resource gibt es ab sofort API-weit nur als Pfad-Form (/categories/{id}, /products/{id}). Begründung: eine kanonische URL pro Ressource, keine Überlagerung mit Bulk-Filtern (?limit/?offset/?status/?language).
  • Unverändert: Range-Form /products/100:200 und Bulk-Pagination (?limit=&offset=).
  • Hinweis (latent): Dasselbe Pluralpfad-Routing-Verhalten existiert strukturell bei suppliers/manufacturers/vouchers/customers/orders/newsdesks/cms/sliders — gesondertes Ticket geplant.

v2.22.0

26. Mai 2026
Slider API: Promotion-Slides als neuer Entity-Typ
  • Neue Endpoints: GET /slider(s), POST/PUT /slider, DELETE /slider/{ID}.
  • Drei neue Scopes: slider:read, slider:write, slider:delete (IDs 56–58).
  • Filter: status, type, categories_id, content_id, language.
  • Nested languages{} mit title, description, direction, width, image, image_mobile, video, video_mobile.
  • Hintergrund: Promo-Slides waren bisher nur via Backend pflegbar. Der neue Endpoint erlaubt z. B. das nachträgliche Korrigieren falscher Slide-Links per PUT /slider.

v2.21.0

19. Mai 2026
Orders Import: Totals-Modi, Auto-Steuer & Drift-Check
  • Auto-Derive von customers_group_id — Kaskade Payload → Kundendatensatz → 0 (Gast).
  • Auto-Derive von tax_flag aus customers_groups.show_tax + customers_groups.tax_exempt (0=Netto, 1=Brutto, 2=Steuerfrei). Explizite Payload-Werte haben Vorrang.
  • Drei Totals-Modi (totals_mode): auto (Default, Caller-Preise vertrauen), recalc (products_price + products_tax aus DB neu laden, Customer-Group- und Land-aware — ideal für Marketplace-Imports mit nur SKU + Menge), verbatim (totals.total wird 1:1 persistiert, ideal für Refunds und historischen Re-Import).
  • End-to-End Drift-Check via expected_total + strict_totals. Bei Abweichung > 0,01 €: Default = Warning, mit strict_totals: true = atomarer Abbruch via OrderBuilderException('TOTAL_DRIFT', 422) vor dem ersten Totals-Insert.
  • Whitelist-Validierung für alle Orders-Entities (orders, order_address, order_product, order_totals, order_external_reference) — unbekannte Keys liefern dedupliziertes UNKNOWN_FIELD-Warning, analog products/customers.
  • PHPUnit-Suite: 17 Tests, 33 Assertions (core/lib/classes/Tests/Order/OrderBuilderTest.php).

v2.20.0

19. Mai 2026
Bestellungen-Endpoint & Generischer Webhook
  • Neuer Endpoint POST/PUT /Import/JSON/orders mit Scope orders:write — Bestellungen via API anlegen und aktualisieren.
  • Generischer Service \\Xonic\\Order\\OrderBuilder (core/lib/classes/Order/OrderBuilder.php) — wiederverwendbar für Marketplace- und WaWi-Importer.
  • Typisierte Exceptions via \\Xonic\\Order\\OrderBuilderException mit errorCode, httpCode und details.
  • Steuerberechnung: Unterstützt Brutto- (tax_flag=1) und Nettomodus (tax_flag=0).
  • Lagerbestand-Buchung via xo_stock_change (Best-Effort: Failures werden als warnings zurückgegeben).
  • External-Reference-Tracking: Automatischer Eintrag in TABLE_ORDERS_EXTERNAL_REFERENCES bei externer ID.
  • Marketing-Reaktion send_webhook — generischer Outbound-Webhook mit HMAC-SHA256-Signatur und angereichertem Payload (Produkt, Bestellung, Kunde, Ticket).

v2.19.0

15. Mai 2026
Media-Endpoint: Statische Asset-Uploads (entity_type=static)
  • Neuer Modus entity_type=static im Media-Endpoint — ermöglicht das Hochladen von Branding-Assets (Logos, Schnittstellen-Bilder) ohne Datenbank-Verknüpfung.
  • Whitelist-Konfiguration: Erlaubte Zielordner über XOPORT_MEDIA_STATIC_FOLDERS (Default: schnittstellen,marketplaces) — verhindert Path-Traversal-Angriffe.
  • Pflichtfelder: entity_type, target_folder, file_name sowie eine Quelle (file_url, file_base64 oder Multipart-Upload).
  • Standardverhalten: Überschreibt vorhandene Dateien (overwrite=true). Antwort enthält den direkten Bild-Pfad.
  • Einschränkungen: Kein DB-Record, keine Thumbnails, kein DELETE, keine Multi-Language-Titel. PUT mit static wird mit STATIC_UPDATE_NOT_SUPPORTED abgelehnt.
  • Use-Case: Marketplace-Logos, Schnittstellen-Branding und statische Frontend-Assets per API verwalten.

v2.18.0

12. Mai 2026
Products Import/Export: Upsells (Warenkorb-Ergänzungen)
  • Neues Feld upsells im Products-Endpoint — analog zu xsells, aber für die „Passt dazu“-Vorschläge im Warenkorb (Tabelle products_upsell).
  • Export: pro Produkt ein Array mit products_model, products_id (Quelle) und sort_order. Single-Product-Export liefert die Upsells des angefragten Produkts (Filter auf Quell-PID).
  • Import via PUT: akzeptiert ein einfaches Array von Modell-Strings (["MODEL-A", "MODEL-B"]) oder ein Array von Objekten ([{"products_model": "MODEL-A", "sort_order": 0}]).
  • Replace-Semantik: bestehende Upsells werden vor dem Insert vollständig gelöscht (DELETE-then-INSERT). Self-Referenzen und unbekannte Modelle werden stillschweigend übersprungen.
  • Use-Case: Befüllung der Warenkorb-Ergänzungs-Vorschläge per API — pro Produkt individuell konfigurierbar (keine globale Default-Liste).

v2.17.0

7. Mai 2026
Products Import: Sonderangebote/Specials erweitert
  • Vollständiger Specials-Block: specials[] pro Produkt mit customers_group_id, direktem Sonderpreis oder discount_percent.
  • Zeitraum und Status: specials_begin, specials_end (Alias expires_date) sowie status/special_status.
  • Angebotsbezeichnungen: specials_price_id oder sprachabhängiges specials_price_name mit automatischem slp-Upsert.
  • Pflege und Löschung: Upsert über (products_id, customers_group_id), Entfernen via action: delete oder delete: 1. Der alte special-Block bleibt abwärtskompatibel.

v2.16.0

29. April 2026
Media API – /media-Endpoint vollständig implementiert
  • Vollständiger CRUD-Support: Export (GET), Import (POST/PUT) und Delete (DELETE) für die Produktbild-Bibliothek (products_images) — bisher waren alle Operationen 501 Not Implemented Stubs.
  • Drei Upload-Modi: file_url (Server-Download via cURL), file_base64 (inline) und multipart/form-data (Field file). Wahl je nach Client und Dateigröße.
  • Validierung: MIME-Sniff (image/jpeg|png|gif|webp), Filesize-Cap via XOPORT_MEDIA_MAX_FILESIZE (Default 10 MB), automatische Kollisions­vermeidung, Filename-Sanitization.
  • Multi-Language-Titel: descriptions[] mit language_id oder language_code pro Bild. PUT mit descriptions ersetzt komplett (Full-Sync-Semantik).
  • Hauptbild-Promotion: Optionales is_main=1 bei POST setzt zusätzlich products.products_image.
  • Thumbnails on demand: URLs für mini/small/medium/large/xlarge werden in jeder Response zurückgegeben — physische Erzeugung erfolgt on-the-fly via image_thumb.php beim ersten Aufruf.
  • Sicheres DELETE: Entfernt Datensatz, Description-Zeilen, Source-Datei und alle Thumbs — aber nur wenn keine andere DB-Referenz mehr auf den Dateinamen zeigt. Bulk-Delete via ?entity_id= oder {ids:[…]}.
  • Neue Scopes: media:read, media:write, media:delete.

v2.15.0

23. April 2026
Products Import: Family/Variants-Felder + SEO-Kollisions-Auto-Korrektur
  • Family/Variants-Felder im Import: products_family (String-Tag), products_family_type (Integer, 3=Varianten-Master-Slave) und products_master (Integer, Master-PID oder Self-Reference) werden jetzt akzeptiert — ermöglicht Master-Slave-Gruppierungen (z.B. Paket-Varianten) via JSON-Import
  • Master-Slave-Workflow: Master anlegen → PID aus records[0].products_id auslesen → zweiter PUT mit products_master = eigene PID für Self-Reference, dann Slaves zuordnen
  • SEO-Uniqueness-Check: Beim Import wird seo_name (bzw. Fallback products_name_as_seo) gegen alle anderen Produkte geprüft — analog zur Backend-Logik
  • Auto-Korrektur: Bei Slug-Kollision wird der Name automatisch zu <slug>-<PID> korrigiert (via tep_search_new_seo()) — der Import bleibt success: true
  • Response-Warnings: Korrekturen werden in warnings[] mit type: "SEO_NAME_CORRECTED" inkl. requested, corrected und conflicting_with.{products_id, products_model, products_name} zurückgemeldet
  • Deckt zuvor bestehende Lücke: Das Backend warnt bei Slug-Kollisionen, die API ließ sie bisher silent durch und produzierte unauffindbare Produkte

v2.14.0

22. April 2026
Products Import: Short-Aliases für Meta-SEO-Felder
  • Neue Kurz-Aliases: title_tag, desc_tag, keywords_tag werden jetzt im Products-Import akzeptiert — konsistent mit der Categories-API
  • Internes Mapping: Die Aliases werden auf die Datenbank-Spalten products_head_title_tag, products_head_desc_tag und products_head_keywords_tag gemappt
  • Vorrang: Full column names haben Vorrang, wenn beide Varianten gesendet werden
  • Rückwärtskompatibel: Bestehende Payloads mit den langen Feldnamen funktionieren unverändert
  • Export: Liefert weiterhin die products_head_*_tag-Felder (nicht die Aliases)

v2.13.0

20. April 2026
SEO-History Auto-Redirects für alle Entity-Typen
  • Bei Namensänderungen via JSON-Import (PUT) werden jetzt für Products (p), Categories (c), Manufacturers (m), News (n) und CMS (s) automatisch seo_history-Einträge angelegt — alte URLs werden via 301-Redirect auf die neuen weitergeleitet
  • Products: Fix — greift jetzt auch bei Rename OHNE explizites seo_name (via products_name_as_seo-Auto-Regenerierung)
  • Manufacturers: sprachunabhängig (ein sh_history-Eintrag, eine description-Zeile pro Sprache mit gleichem Slug)
  • News & CMS: sprachspezifisch (wie Categories/Products)
  • Idempotent: gleiche Rename-Payload erzeugt keinen zweiten Eintrag
  • Helper persistSeoHistory() + buildSeoHistoryDeletestamp() in ImportJSON extrahiert (DRY)

v2.12.0

20. April 2026
Categories Productlist (Lightweight Endpoint)
  • Neuer Sub-Endpoint: GET /categories/productlist — Lightweight Mapping von Kategorien zu Produkt-IDs und Models
  • Query-Parameter: ?categories_id=, ?status=, ?include_inactive_products=, ?language=
  • Response: Pro Kategorie categories_id, categories_name, products_count und products[] Array mit id + model
  • Memory-effizient: Ein einziger SQL-Query statt 12+ Relations
Products SQL-Level Pagination
  • Fix: GET /products verwendet jetzt SQL-Level Pagination (LIMIT/OFFSET direkt im Query)
  • Vorher: Alle Produkte + Relations in den Speicher geladen, dann array_slice()
  • Nachher: Nur die angeforderte Seite wird aus der Datenbank geladen — kein Memory-Limit mehr
  • Einzelabruf via /products/{id} und Ranges unverändert

v2.11.0

13. April 2026

  • Tickets Full CRUD: Import (POST/PUT) und Delete Endpoints für Tickets. tickets:write und tickets:delete Scopes jetzt aktiv.
  • Export: linked_appointments[]: Verknüpfte CRM-Termine werden jetzt pro Ticket im Export mit ausgeliefert (Termin-ID, Start/Ende, Titel, Beschreibung, Ort).
  • Import POST (Create): Pflichtfelder ticket_subject, ticket_customers_email, ticket_customers_name. Generiert ticket_link_id, erstellt Upload-Verzeichnis, optionaler initialer Kommentar + Admin-Zuweisungen.
  • Import PUT (Update): Status/Priorität/Abteilung ändern, interne Kommentare hinzufügen (ticket_internal_comment=1 immer). admin_id ist Pflichtfeld bei Kommentaren (0 = System).
  • Delete: Kaskadierende Löschung (status_history → admins → followers → appointments_relationships → CRM followers → Dateien → ticket).
  • Scopes: tickets:write (Import/Update), tickets:delete (Löschen) – zuvor als „geplant" markiert, jetzt voll funktionsfähig.

v2.10.0

9. April 2026

  • Globale Pagination: Alle Export-Endpoints unterstützen ?limit=n&offset=n. Default-Limit 50, Hard-Cap 250 (konfigurierbar via XOPORT_API_MAX_LIMIT / XOPORT_API_DEFAULT_LIMIT). Response enthält stats.count, stats.limit, stats.offset, stats.has_more.
  • Tickets – Neue Filter: ?admin_id=n für zugewiesene Tickets (INNER JOIN auf ticket_to_admins) und ?created_by_admin_id=n für Ersteller-Filter.
  • Memory-Schutz: Auto-Pagination bei großen Datasets verhindert Memory-Exhaustion.

v2.9.0

8. April 2026

  • Tickets API (Export): Neuer Endpoint GET /tickets und GET /ticket/{id} für Ticket-Export mit vollständiger Konversationshistorie, zugewiesenen Admins und Followern.
  • Attachment-Endpoints: GET /ticket_attachments/{id} für Metadaten-Liste (Name, Größe, MIME-Type) und GET /ticket_attachment/{id}?file=name für Binary-Download mit Path-Traversal-Schutz.
  • Query-Filter: ?status=, ?priority=, ?department=, ?customer_id=, ?type=, ?date_from=&date_to=, ?last_modified=, ?language=
  • Neue Scopes: tickets:read (implementiert), tickets:write, tickets:delete (implementiert in v2.11.0)
  • Meta-Daten: ticket_statuses, ticket_priorities, ticket_departments in jeder Response

v2.8.1

11. März 2026

  • Bugfix: Categories last_modified: Wird jetzt bei PUT-Updates immer gesetzt — auch wenn nur languages-Daten (Beschreibung, Name etc.) aktualisiert werden. Zuvor wurde last_modified nur aktualisiert, wenn Haupttabellen-Felder (status, parent_id etc.) mitgesendet wurden.
  • Newsletters ALLOWED_FIELDS: languages zur erlaubten Feldliste hinzugefügt — kein UNKNOWN_FIELD-Warning mehr bei Multi-Language Newsletter-Import.

v2.8.0

6. März 2026

  • Newsletter Subscribers API: Neue Endpoints für Newsletter-Abonnenten (Export/Import/Delete) mit DOI-Support.
  • Newsletter Campaigns API: Neue Endpoints für Newsletter-Kampagnen (Export/Import/Delete) mit Multi-Language-Format.
  • Neue Scopes: newsletter:read, newsletter:write, newsletter:delete, newsletters:read, newsletters:write, newsletters:delete
  • DOI-Support: SHOP_NEWSLETTER_DOUBLE_OPT_IN Konfiguration wird respektiert, Bestätigungs-E-Mail bei aktiviertem DOI.
  • Provider-Sync: Automatische Synchronisation mit externen Providern (Cleverreach, Mailchimp, Optimizely) via xoNewsletter.
  • DSGVO-Compliance: Privacy-Logging bei Import/Delete, Cascade-Löschungen dokumentiert.
  • Safety-Checks: Locked-Kampagnen geschützt (409 LOCKED), versendete Newsletter nicht löschbar (409 HAS_SENT_ENTRIES).
  • Subscriber-Filter: ?status=confirmed|unconfirmed, ?email=, ?customer_id=, ?language_id=, ?last_modified=
  • Campaign-Filter: ?status=, ?language_id=
  • Bulk-Delete: Mehrere Abonnenten per JSON-Body (by ID oder E-Mail) löschbar

v2.7.0

25. Februar 2026

  • Contracts API: Neue Endpoints für Verträge/Abos (Export/Import/Delete).
  • Status-Endpoint: Dedizierter PUT /contracts/{id}/status Endpoint für Statusänderungen.
  • Neue Scopes: contracts:read, contracts:write, contracts:delete, contracts:write
  • Query-Filter: ?status=, ?customer_id=, ?product_id=, ?interval=, ?date_from=&date_to=, ?active=1
  • Nested Data: Attributes, Domains, Status-History, Orders
  • Cascade-Delete: Attribute → Domains → Bestellverknüpfungen → Status-History → Vertrag

v2.6.0

24. Februar 2026

  • SEO History API: Neue Endpoints für SEO-Weiterleitungen (Export/Import/Delete).
  • Auto-Redirect: Bei seo_name-Änderungen im Product/Category JSON-Import werden automatisch SEO-History-Einträge erstellt (analog XML-Import).
  • Neue Scopes: seohistory:read, seohistory:write, seohistory:delete
  • Query-Filter: ?type=, ?base_id=, ?language=, ?expired=1
  • Bulk-DELETE: Per JSON-Body oder Query-Filter löschen

v2.5.0

23. Februar 2026

  • Erweiterte Produkt-Felder: 19 neue Felder für Vertrags- und Logistik-Daten im Product Import.
  • Vertragsfelder: contract, contract_use_current_price, contract_option — Vertragsprodukte können jetzt vollständig per API angelegt werden.
  • Logistik-Felder: products_cost, products_inventory_management, products_min_qty, products_max_qty, products_qty_blocks, products_base_price
  • Artikel-Felder: products_sort_order, products_ean, products_free_shipping, products_image
  • Flags: service, sitemap, non_cart, non_price
  • Zuordnungen: shipping_profile, products_unit_id, base_unit_id

v2.4.2

19. Februar 2026

  • Product Import: Nested languages-Format: Konsistent mit Export und allen anderen Entities (Categories, Newsdesks, CMS). Akzeptiert {"languages": {"de": {...}, "en": {...}}} mit ISO-Codes oder numerischen IDs.
  • Auto-Fill bei INSERT: Fehlende Sprachen werden automatisch mit Master-Language-Daten befüllt.
  • Flat-Format entfernt: products_name + language_id auf Top-Level wird nicht mehr unterstützt.
  • Symmetrischer Roundtrip: Export → Import → Export ergibt identische Daten.

v2.4.1

19. Februar 2026

  • SEO-Felder im Product Import: 7 neue Felder: products_head_title_tag, products_head_desc_tag, products_head_keywords_tag, seo_name, products_url, products_checkout_description, products_image_title
  • Ermöglicht vollständigen Export→Import Roundtrip für alle Produkt-Beschreibungsfelder.

v2.4.0

6. Februar 2026

  • CMS Endpoint: Neuer Entity-Typ cms mit vollständigen CRUD-Operationen (Export/Import/Delete).
  • Neue Scopes: cms:read, cms:write, cms:delete
  • Safety-Checks: System-Seiten (IDs 1-999) geschützt, content_lock respektiert
  • Query-Parameter: ?status=, ?module=, ?language=
  • Multi-Language Support mit verschachteltem languages-Format

v2.3.0

31. Januar 2026

  • Suppliers CRUD: Neue Endpoints für Lieferanten (Export/Import/Delete)
  • Product Export erweitert: videos, suppliers, contenteditor Felder
  • Product Import erweitert: categories_ids, groups, specials, extra_fields, badges, xsells, tabs, videos, suppliers, contenteditor
  • Manufacturer Import: Alle GPSR-Felder (EU + Non-EU Kontaktdaten)
  • Neue Scopes: suppliers:read/write/delete
  • Stubs: Marketing, Newsletter, Media Endpoints (501 Not Yet Implemented)

v2.2.0

27. Januar 2026

  • Unknown Field Warnings: Response enthält jetzt ein warnings Array für unbekannte/ignorierte Felder
  • Dedupliziert: Jedes unbekannte Feld erscheint nur einmal, auch bei 100+ Records im Bulk-Import
  • Nested-Fields: Prüft auch languages.* und addresses[] Objekte
  • Nicht-blockierend: Request wird trotz Warnings erfolgreich verarbeitet (Postel's Law)
  • Warning-Objekt: {type, field, entity, message, first_occurrence}

v2.1.0

27. Januar 2026

  • RESTful HTTP-Methoden-Trennung:
    • POST = nur INSERT (neue Datensätze anlegen)
    • PUT = nur UPDATE (existierende Datensätze aktualisieren)
  • Strikte Validierung: POST mit existierender ID → 409 Conflict, PUT mit unbekannter ID → 404 Not Found
  • Neues Response-Feld: records Array mit {index, entity_id, status} für jeden Datensatz
  • HTTP 405 Method Not Allowed bei falscher Methode (z.B. DELETE auf Export-Endpoint)
  • Alle 8 Entity-Endpoints unterstützen POST/PUT: categories, products, customers, manufacturers, newsdesks, newsdeskcats, faq, faqcats

v2.0.3

26. Januar 2026

  • DELETE Endpoints für newsdesks und newsdeskcats
  • Safety-Checks: Kategorien mit Unterkategorien oder verknüpften Artikeln können nicht gelöscht werden
  • Fehler-Codes: HAS_SUBCATEGORIES, HAS_ARTICLES
  • Cache-Löschung nach DELETE

v2.0.2

26. Januar 2026

  • Newsdesk categories_ids im Export und Import
  • News-Artikel erhalten Kategorie-Zuordnungen (wie Produkte)
  • Cache-Löschung nach newsdesks/newsdeskcats Import
  • Newsdesks/Newsdeskcats Import mit verschachteltem languages-Format

v2.0.1

26. Januar 2026

  • Categories Import mit verschachteltem languages-Format
  • Sprach-Keys als Code (de, en) oder ID verwendbar
  • OAuth2 Clients Admin-UI mit Live-Suche und Sortierung
  • Payload-Logging mit Request/Response-Body

v2.0.0 Breaking

26. Januar 2026

  • BREAKING: OAuth2 Client Credentials (RFC 6749)
  • API Keys entfernt - Migration zu OAuth2 erforderlich
  • Neue Endpoints: POST /oauth/token, GET /me
  • Neue Tabellen: xoport_oauth_clients, xoport_oauth_tokens
  • Token-Präfix: xoat_, Client-ID-Präfix: xoc_
  • Backend: Werkzeuge → xoPort OAuth2 Clients

v1.5.0

26. Januar 2026

  • FAQ Endpoints (faq/faqcategories Export/Import/Delete)
  • /me Endpoint für Scope-Introspection

v1.4.x

23. Januar 2026

  • DELETE Endpoints für Kategorien, Produkte, Hersteller, Kunden
  • Safety-Checks gegen versehentliche Löschung
  • Bugfix: Double-Escaping bei HTML-Attributen behoben

v1.0.0 - v1.3.x

23. Januar 2026

  • Initiale JSON API mit Export/Import
  • Multi-Address-Management für Kunden
  • Categories Import mit Auto-seo_name
  • Language Filter, ISO-2 Country Codes
  • API Key Authentifizierung (deprecated in v2.0)

Immer aktuell bleiben

Bei Breaking Changes informieren wir Sie über den Newsletter.