API Change Log

Version History · Changelog

API Changelog

An overview of all changes and improvements to the xoPort REST API.


Versions

v2.122.6 Current

October 6, 2026
Export Product Reviews
  • New: GET /reviews, GET /review/{reviews_id}, and the section GET /reviews/{von}:{bis} with the scope products:read. Previously, the API only returned the average rating and the number of reviews for a product (products_rating, products_rating_count).
  • Per review: reviews_id, products_id, products_model, reviews_rating, status, promo_status, author, verified_purchase, helpful_count, review_external_id, date_added, last_modified, and descriptions[]; per language with reviews_title, reviews_text, and reviews_reply (response from the store).
  • Filters: products_id (also as a comma-separated list), status (1 = approved, default; 0 = awaiting approval; all) and since (created or last modified on or after). You can navigate through the list as with products using limit and page.
  • No customer data: author is the name that also appears on the product page. The endpoint does not return the customer ID, email, or order number—only verified_purchase. As of Shop 4.9.153.

v2.122.5

October 1, 2026
XML Interface: Product Export and Suppliers
  • The XML product export would terminate with an HTTP 500 error as soon as an item was not yet included in the quick search index. These items are now exported normally; the JSON export was not affected.
  • The XML product import now synchronizes the “ suppliers ” node instead of deleting and recreating suppliers. The supplier item numbers for permanent suppliers are retained. As of Shop 4.9.139.

v2.122.4

September 30, 2026
Features and Tickets
  • Features: ` options[].cost ` is now an alias for ` ext_cost`. Previously, a purchase price written this way ended up in a location that neither the product page, checkout, nor editor could read. If both are sent, ` ext_cost` takes precedence. Purchase price and MSRP are validated as amounts; invalid values trigger the warning ` OPTION_META_INVALID`.
  • Tickets: DELETE /tickets now removes all data associated with the ticket, such as assignees, subscribers, and date and project links. Starting with Shop 4.9.135.

v2.122.3

September 30, 2026
Creating Orders: Prices for Gross Calculation
  • orders:write with tax_flag = 1: Prices determined by the shop (recalc) and shipping from compute_shipping are net values. Previously, they were counted as gross, and the tax was missing from the total. Now they are adjusted accordingly. Prices that you enter yourself remain gross, as before.
  • Anyone who has adjusted expected_total to account for the total being too low will now receive the warning TOTAL_DRIFT (with strict_totals HTTP 422).
  • Formula surcharges for standard attributes (apply_default_attributes) are calculated as shown on the product page. Previously, a formula resulted in 0 €. Formulas that can only be evaluated on the product page trigger the warning “ DEFAULT_ATTRIBUTE_FORMULA.” Starting with Shop 4.9.134.

v2.122.2

September 29, 2026
Customers: Partially change addresses without the country field reverting
  • PUT /Import/JSON/customers Using ` addresses[] ` and ` address_book_id ` (or the ` address` object) now writes only the fields that were sent. Previously, any update without a country set the country to the shop’s country and the zone to 0. A simple street update would thus turn an Austrian address into a German one.
  • The country and zone only change if you include them in the request. If the country is unknown, the response reports the warning ADDRESS_COUNTRY. An update then retains the stored country, while a new address receives the store’s country as before.
  • Also fixed: Apostrophes were saved with a backslash, and values that were too long were truncated byte by byte without warning. Now they are truncated character by character, with the warning “ ADDRESS_TRUNCATED.” Numbers in “ entry_country ” are not considered country IDs.
  • Shops using older versions: Always include the country when sending address updates. Starting with Shop 4.9.132.

v2.122.1

September 27, 2026
Security: Customer and order exports without password data
  • The exports /customers and /orders (JSON and XML) no longer include the password hash or the tokens for password reset and account deletion: customers_password, customers_password_request, customers_password_request_time, and customers_hash have been removed without replacement.
  • Furthermore, the customer record no longer contains an “ iban ” or an “ bic.” Both fields remain in the order record. Starting with Shop 4.9.123.

v2.121.0

September 19, 2026
News Import Creates Missing Languages
  • POST /Import/JSON/newsdesk When creating a new entry, it supplements every active language for which no separate block was sent using the master language (the ` de` block, or otherwise the first block sent). If DeepL translation is set up, it translates these languages during the next run. Previously, an item created using only ` de ` remained permanently monolingual.
  • Modified texts in the nested ` languages` format are now also retranslated. Starting with Shop 4.9.106.

Version numbers 2.119.1 and 2.122.0 relate to internal endpoints; 2.120.0 has not yet been released.

v2.119.0

September 18, 2026
REST API: Always requires a token; optionally restricted to fixed addresses
  • Important: IP authorization for the XML interface (XML_PORT_IP_FILTER) no longer grants access to the JSON/REST API. REST always requires an OAuth2 token, and its scopes apply. A client that previously used REST without a token solely via an authorized address will receive the message “ 401 ” along with “ reason: deny_token_required.”
  • A new setting, ` XOPORT_REST_IP_LIST `, has been added as an additional security measure. If this field is filled in, a request must originate from a listed address and include a valid token; otherwise, the response will be ` 403 ` with ` reason: deny_ip_not_listed`.
  • If the API rejects a request, it specifies the reason in the ` reason ` field and in the ` X-XoPort-Deny-Reason` header. The XML interface remains unchanged. As of Shop 4.9.83.

v2.118.1

September 17, 2026
Tickets: No more false warnings for linked appointments
  • linked_appointments The fields ` POST` and `PUT /Import/JSON/tickets ` previously also reported the warning ` UNKNOWN_FIELD ` (“will be ignored”), even though the appointments have been created since v2.43.0. The warning has been removed; processing remains unchanged. As of Shop 4.9.75.

v2.118.0

September 17, 2026
Tickets: Processing Claim
  • New endpoint PUT /Import/JSON/ticket_claims (scope tickets:write) shows who is currently working on a ticket. Required fields: ticket_id, admin_id, and state (claimed or released); optional fields: ttl_minutes (default 60, 5 to 240), label, and force.
  • Result per record in records[].status: claimed, refreshed, taken_over, released, unchanged, or conflict. ⚠ A conflict is counted as failed.
  • GET /tickets and GET /ticket/{id} return the new fields claim and edit_lock (the ticket is currently open in the backend), each returning null if nothing is available.
  • A public response (via API or in the backend) and changing the status to “Closed” automatically release the claim; an internal note does not. The claim is a note, not a lock, and does not change either ticket_date_last_modified or the history. Starting with Shop 4.9.73.
  • Version numbers v2.116.0 and v2.117.0 are part of new features that will be released with the next feature release; they will be added here at that time.

v2.115.0

September 15, 2026
XML importers check the feed file upon loading
  • If a file contains characters prohibited by XML 1.0 (e.g., control characters in descriptive texts), all eight XML importers remove these characters and process the file. Previously, such a file would return 0 records, yet it was archived anyway, and the response reported success.
  • If a file still cannot be read after this, it is moved to xoport/xml/import/error/ instead of being archived and is deleted there, just like the archive, after the retention period.
  • New response fields: outcome (ok, partial, failed), files.failed, and warnings.invalid_xml_chars_removed. files.processed.filenames no longer lists rejected files.
  • ⚠ If all files in a run fail, the import responds with ` "state": "error" ` and HTTP 422. Integrations that evaluate the HTTP status will see such a failure for the first time; you can identify individual rejected files by ` outcome: "partial"`. Starting with Shop 4.9.65.

v2.114.0

September 14, 2026
Excluded Products on Coupons
  • GET /coupons and GET /coupon/{id} provide the new field restrictions.excluded_products: the product IDs that never receive a discount from the coupon.
  • This exclusion applies in addition to all other restrictions and takes precedence over ` restrictions.products`. An excluded main item includes its variants, while an excluded variant includes only itself.
  • Coupons without exclusions return an empty list of ` []`.

v2.113.1

September 14, 2026
Coupons: restrict_mode corrected
  • ⚠ restrict_mode was previously exported incorrectly. The correct definitions are: allow_only = only items in the listed categories, deny = all except these, unless the item is also in an unlisted category, and now deny_strict = all except items that are in any listed category.
  • This field exclusively describes restrictions.categories. restrictions.products and restrictions.manufacturers are always positive lists; if the product list is populated, categories and manufacturers are not evaluated.
  • Anyone who has been evaluating this field since v2.102.0 should check their assignment.

v2.113.0

September 13, 2026
Assign invoice numbers via the API
  • New field ` assign_invoice_number ` in ` POST ` and ` PUT /Import/JSON/orders`: Using ` true `, the store assigns the next number from its invoice number range. The number itself cannot be set and remains unchanged thereafter.
  • The response returns invoice_number_assigned; if successful, it also returns invoice_id_individuell and invoice_date. Otherwise, invoice_number_reason specifies the reason (mode_disabled, already_assigned, locked), accompanied by the warning INVOICE_NUMBER_NOT_ASSIGNED.
  • Simultaneous assignments are blocked from occurring. If you receive the error “ locked,” the shop intentionally does not assign a number instead of a duplicate one—please resubmit the request.

v2.112.0

September 13, 2026
New categories with the specifications from the backend form
  • ⚠ If you create a category without sitemap or shop_ids, the same values now apply as in the backend form: sitemap = 1 and all shops listed in full. Previously, the column settings (no sitemap, no shop assignment) took precedence.
  • The warning “ CATEGORY_DEFAULTS_APPLIED ” lists the set values. Updates to existing categories are not affected.

v2.111.0

September 13, 2026
Remove videos and tracking numbers
  • videos[]: Remove individual entries using _action: "delete", or use videos_replace: true to apply the sent list as the complete status (only upon explicit request). The video file itself remains intact; only the association is removed.
  • trackings[]: _action: "delete" via id_tracking or service_provider and code. This allows you to remove and recreate a tracking number in a single step.
  • Both methods verify that the entry belongs to the specified product or order.

v2.110.0

September 13, 2026
Attributes of an existing order line item
  • products_mode: "update" attributes now accepts this data. The sent list completely replaces the item’s attributes, while removes all of them. []
  • A different surcharge does not change the line item price—the interface issues a warning in this case. If the price is to change, cancel the line item and re-add it.
  • Changes are locked for orders that have already been invoiced because the characteristics appear on the invoice and delivery note.

v2.109.0

September 13, 2026
Read and process CSV/XLS files
  • New: GET /Export/JSON/porters, /porter/{id}, and PUT /Import/JSON/porters with the scopes porters:read and porters:write.
  • active Sets the status (true = active thereafter) instead of toggling it. Without active, the request is rejected. When deactivated, the shop removes the generated export file as in the backend.
  • The one-time query at columns returns the complete column mapping for the Porter.

v2.108.0

September 13, 2026
Read and Change Settings
  • New: GET /Export/JSON/configuration and PUT /Import/JSON/configuration with the scopes configuration:read and configuration:write.
  • ⚠ Only keys that the administrator shares in XOPORT_CONFIG_API_KEYS are visible—the default is empty. Login credentials, license, and session settings remain locked even when shared.
  • Only existing keys can be modified, and every change is logged. The warning SHOP_OVERRIDE_ACTIVE indicates a domain override that overrides the modified default value.

v2.107.0

September 13, 2026
Processor in Order History
  • user_id for an orderPUT enters the responsible agent into the history entry. Only an existing agent ID is accepted.
  • customer_notified is still written, but the interface reports via NO_MAIL_SENT that it does not send a status email itself.

v2.106.0

September 13, 2026
Upload product videos, option images, and subfolders
  • Media upload via entity_type: "product_video" (up to 100 MB) and entity_type: "option" for option images.
  • subfolder Saves images in a subfolder (one level deep, lowercase letters, numbers, _, and -).
  • The file extension is determined based on the recognized file type, not the file name sent.

v2.105.0

September 13, 2026
The product export can be sent back
  • 68 additional product fields are writable, including tiered pricing with quantity thresholds, marketplace toggles, FSK 18, bulky goods, MSRP, bonus points, and the purchase date.
  • 14 read-only fields are reported as “ READONLY_FIELD ” instead of being discarded without comment.
  • ⚠ Invalid values (unknown selection value, text too long, unreadable number) reject the data record instead of writing it incompletely. Yes/No fields sent as empty remain unchanged.

v2.104.0

September 13, 2026
Replaced product images receive new preview images
  • If an image is replaced with the same filename, the shop removes the associated preview images in all formats. The response reports thumbnails_removed and replaced_existing.
  • Entries in videos[] are checked for unknown field names; with ?_strict=true, a typo results in an HTTP 400 error.

v2.103.0

September 13, 2026
Ticket Departments
  • New: GET /Export/JSON/ticket_departments (?active=), /ticket_department/{id}, POST/PUT /Import/JSON/ticket_departments, and DELETE /Delete/JSON/ticket_department/{id}.
  • Departments are multilingual; empty language fields default to the name of the default language.
  • If a department still has a ticket assigned to it, deleting it returns the response 409 DEPARTMENT_IN_USE. ?reassign_to={id} reassigns the tickets first; ?force=1 deletes them regardless.

v2.102.0

September 13, 2026
Vouchers and Coupons as JSON
  • New: GET /Export/JSON/coupons and /coupon/{id} with the filters ?code=, ?type=, ?active=, ?kind=, and ?valid_on=.
  • Restrictions are provided as ID lists, along with the readable fields restrict_mode and is_credit_voucher. " 0 " in the redemption limits indicates "unlimited."
  • Fixed: The name and description of a coupon were previously missing from the export.

v2.101.0

September 13, 2026
The XML export checks permissions
  • ⚠ An OAuth2 token with a restricted scope now receives " 403 " in the XML export for areas without authorization: customers:read, orders:read (also for coupons), products:read, categories:read, manufacturers:read.
  • Connections via AppKey or IP authorization are not affected.

v2.100.0

September 13, 2026
Filter configuration for category pages
  • New: GET /Export/JSON/products_filters and /products_filter/{fid} with ?options_id= and ?categories_id= – which filters a category page offers.
  • Note: 2.100.0 follows 2.99.0. Compare versions using version_compare(), not as a decimal number.

v2.99.0

September 13, 2026
Customer export with filters and pagination
  • New filters at /customers: customers_id, customers_group_id, language_id, email, lastname, company, customers_id_extern, the prefix filters email_prefix, lastname_prefix, company_prefix, as well as purchased_without_account, sort, order, and page.
  • Large customer datasets are paginated in the database rather than loaded in their entirety.
  • An invalid filter value results in 400 INVALID_FILTER_VALUE instead of being silently ignored.

v2.98.0

September 12, 2026
Tracking numbers, rating requests, and item fields
  • trackings (plural, as in the export) is assumed. Duplicate shipment numbers are skipped and reported; a missing link is added from the shipping carrier.
  • rating_mail is writable.
  • products_mode: "update" Modifies fields of existing line items (purchase price, name, item number, EAN, unit, shipping profile, date fields). Quantity, prices, and tax rate are rejected.

v2.97.0

September 12, 2026
Remove ticket attachments, resend email
  • New: DELETE /Delete/JSON/ticket_attachment/{tshid}?filename=.
  • notify_history_id Sending to PUT /Import/JSON/tickets resends the reply email for an existing history entry without creating a new one.
  • apply_value_defaults When a characteristic is assigned for the first time, it adopts the default settings of the option value (upon explicit request, because the defaults may incur an additional charge).

v2.96.0

September 12, 2026
Attributes and option values as separate endpoints
  • New: GET /Export/JSON/products_options, /products_option/{id}, /products_options_values, and POST/PUT /Import/JSON/products_options.
  • values[] This is not a complete list: only entries containing “ _action: "delete" ” are removed; “ _action: "attach" ” splits an existing value.
  • ⚠ An unknown option_id in the product import is now rejected with OPTION_NOT_FOUND, instead of silently creating a characteristic.

v2.95.0

September 12, 2026
Delete individual history entries
  • New: DELETE /Delete/JSON/order_status_history/{id} and DELETE /Delete/JSON/ticket_status_history/{id}. The status of the order or ticket is updated as shown in the form.
  • The oldest entry for an order is protected (409 FIRST_HISTORY_ENTRY).

v2.94.0

September 12, 2026
Delivery Time Profiles
  • New: /shipping_profiles and /shipping_profile/{id} for reading and writing, with levels, delivery time ranges, and customer texts for each language.
  • For each product, the export also provides shipping_profile_effective, _name, _step, and _text.

v2.93.0

September 12, 2026
Quick Search and Addressing via Product ID
  • Products created or modified via the interface are included in the quick search. The export displays the index status for each language at quicksearch.
  • Fixed: A PUT with products_id could result in an HTTP 500 error, and address changes did not trigger a redirect when performed this way.

v2.92.0

September 12, 2026
Configurator option rules
  • options_rules[] on the product, both read and write. If the field is missing, existing rules remain unchanged.
  • Active rule blocks are checked for completeness before writing; without the corresponding license, the operation is rejected.

v2.91.0

September 12, 2026
Status history and document texts for each order
  • The order export provides status_history[] with all history entries.
  • indiv_text0 indiv_text4 (replacement texts per document) as well as and (filenames of stored PDFs) are writable. indiv_rg indiv_ls
  • Warnings from an orderPUT now appear next to the corresponding entry in records[].

v2.90.0

September 12, 2026
Tax Classes and Ticket Details
  • New: GET /Export/JSON/tax_classes and /tax_class/{id} now display tax rates for each tax zone.
  • The ticket history provides the message ID of received emails and ticket_event_type.
  • Fixed: Simply changing the video title resulted in an HTTP 500 error.

v2.89.0

September 12, 2026
Partial successes are reported
  • Fixed: When attaching line items, a language code might appear on the invoice instead of the control title.
  • Without a valid admin_id, a ticket history entry is no longer created.
  • outcome Reports partial when part of the order was rejected. Also new is ?order_id= sent to /tickets.

v2.88.0

September 12, 2026
Multilingual Product Tabs
  • tabs is supported in the flat, language-nested, and export formats; the export provides one ` languages` block per tab.
  • New categories without sitemap/shop_ids are reported as CATEGORY_DEFAULTS_APPLIED.

v2.87.0

September 12, 2026
Fees and order reference are writable
  • handling_fee, transaction_fee, selling_fee, other_fee, and po_number can be set via POST and PUT.
  • Amounts outside the valid range and references that are too long are rejected rather than truncated. A new filter is available at ?po_number= and /orders.

v2.86.0

September 12, 2026
Pagination and Sorting
  • ?sort= and ` ?order= ` to ` /orders ` and ` /products`; ` ?page= ` has been implemented and is confirmed as ` stats.page `.
  • page Combined with offset, this results in an error. A truncated limit entry returns LIMIT_CAPPED.

v2.85.0

September 12, 2026
Special prices now fully included in export
  • Special prices without a label were previously missing from the export. Now all fields are included, including customer group, time period, label, and type.
  • Newly created products have the " products_last_modified " label and thus appear in the edit filter.

v2.84.0

September 12, 2026
Category locks are writable
  • subs_block and “ tree_block ” are writable. ⚠ “ subs_block ” affects redirects and the sitemap for the entire category branch.

v2.83.0

September 12, 2026
All export endpoints check their filters
  • Unknown filters are reported at each export endpoint with ` UNKNOWN_FILTER `; the call is aborted with ` ?_strict=true `.

v2.82.0

September 12, 2026
The manufacturer based on the name
  • manufacturers_name and the manufacturer block of the export are resolved. Unknown or ambiguous names are rejected, not created anew.
  • Documents which data is removed by ` DELETE /product `—often ` products_status = 0 ` is the better choice.

v2.81.0

September 12, 2026
Special prices and additional fields can be returned
  • special Accepted as a list or single object; slp_value is used as the label name.
  • extra_fields Appears as an empty list even without entries.

v2.80.0

September 12, 2026
Restore without data loss
  • Fixed: Category assignments in the export form moved products to Category 1.
  • Image lists are checked first and then written; a main image remains the main image.

v2.79.0

September 12, 2026
Partially update categories
  • A categoryPUT no longer requires the language block.
  • categories_mode: "append" Adds assignments to the product; the full sync reports removed assignments using CATEGORY_ASSIGNMENTS_REPLACED.

v2.78.0

September 12, 2026
Assign a ticket to a customer via PUT
  • ticket_customers_orders_id, ticket_customers_id, ticket_customers_name, and ticket_customers_email are written to PUT. The email address cannot be left blank.

v2.77.0

September 12, 2026
All or Nothing During Import
  • ?_strict=true Completely rejects products, categories, customers, and orders with unknown fields (400 UNKNOWN_FIELD) before anything is written.

v2.76.0

September 12, 2026
Address products by ID
  • A ` PUT ` with ` products_id ` is sufficient. Duplicate product IDs are rejected instead of matching any random product.

v2.75.0

September 12, 2026
Exact product filters
  • New: products_model and products_ean as exact filters; products_id as a filter; manufacturers_id now works.
  • If a single ID in a plural path returns multiple records, this is indicated by RANGE_START_NOT_SINGLE_RECORD.

v2.74.0

September 12, 2026
Tickets: No sending without an attachment
  • If an attachment is rejected, no customer email is sent; the comment remains saved.
  • New reply field ` outcome ` (`ok`, ` partial`, ` failed`) as a reliable success criterion.

v2.73.0

September 12, 2026
Numbers in JSON without silent data loss
  • If the number recognition feature converts values (e.g., leading zeros), this is indicated by ` NUMERIC_CHECK_APPLIED`. ` ?numeric_check=false ` returns such values unchanged as text.

v2.72.0

September 12, 2026
Document retrieval without side effects
  • Fixed: Retrieving an invoice correction via order_document could generate a correction number. If no correction exists, the endpoint now responds with 409.
  • Unused fields in an order (PUT ) return the response FIELD_IGNORED_ON_UPDATE. Payment method, billing address, and shipping address can be corrected via PUT.

v2.71.0

September 12, 2026
Read and write attributes in full
  • ⚠ Existing attribute assignments are updated rather than skipped; fields that were not sent remain unchanged.
  • options_replace Synchronizes using a difference comparison—option screens, base price factor, and cost price are no longer lost. _action: "delete" removes a single assignment.
  • Newly created attributes are reported via ` OPTION_CREATED`; ` options_strict_names ` rejects unknown names instead.

v2.70.0

September 12, 2026
Deletion now requires the DELETE method
  • ⚠ Please check your connection. The endpoints at Delete/JSON/ now accept only the HTTP method DELETE. The API responds to any other method with 405 METHOD_NOT_ALLOWED and the header field Allow: DELETE —before anything else happens.
  • Why this was necessary: Previously, the API did not check the method at all at this point. A simple call to the address in a browser, a link preview, or a search engine bot could therefore delete a record. It was sufficient for an access token to appear just once in an address.
  • Impact in practice: likely none. We analyzed the access logs prior to the change—all observed delete requests were already using DELETE. However, if your program uses GET or POST, please update it.
Error messages are now analyzable
  • Unknown addresses, invalid number ranges, and nonexistent endpoints now respond with JSON and the correct HTTP status code (404, 400, 501) instead of a single line of text. Previously, depending on the operating mode of the PHP environment, the server would return the code ` 200 `—meaning that error-handling code that evaluates the status code would consider the request successful.
  • The error objects follow the usual structure: error.code (NOT_FOUND, INVALID_RANGE, NOT_IMPLEMENTED) and error.message.

v2.69.0

September 7, 2026
Recipients in the CC/BCC fields are now editable
  • New field “ receivers ” at POST/PUT /ticket(s): { mode, cc, bcc }. Previously, recipients could only be viewed—editing was only possible in the shop’s interface.
  • The “ add ” mode (default) adds to the list; “ replace ” replaces only the provided lists; “ remove ” removes individual addresses. Addresses are validated as in the backend; a list containing only invalid addresses leaves the list unchanged—a typo should not clear the recipient list.
  • This process runs before comments are added and the email is sent as part of the same call: “Add address and reply” is thus completed in a single call.

v2.68.0

August 31, 2026
Main Category per Product
  • New field “ main_category_id ” on the product entity. It determines which of the multiple category assignments provides the category path in the URL, the canonical URL, the entry in the sitemap, and the category information in the price portal feeds.
  • ⚠ The import semantics intentionally differ from categories: if the field is missing, the existing data remains unchanged. Otherwise, any existing program that updates products would delete the main category on its first run. The value 0 overrides this behavior.
  • A category that is not assigned to the product at all is rejected and reported—an import must not create an assignment that points to nothing.

v2.67.0

August 26, 2026
Warranty type is now importable
  • New field “ warranty_type ” in the product import. Valid values are “ physical ” and “ digital ”; the spelling has been corrected. The usual “ array_key_exists ” semantics apply: if the key is missing, the column remains unchanged; an empty string clears it.
  • The export has always included this field —only the import didn’t recognize it. This made a “export, modify, and write back” roundtrip impossible. Version 2.63.0 hadalready closed this same gap for the inventory fields.
  • ⚠ An invalid value is rejected, not corrected. You’ll receive a warning in warnings[], and the existing value remains unchanged. This is intentional: The field helps determine whether the store treats an item as physical merchandise or digital content—and thus dictates the required warranty information.
Fixed without an API contract, but as part of the same update
  • CSV Import: warranty_period is now validated against the allowed values. Previously, free-text entries were simply converted to lowercase and then saved—modified, but still invalid. The same applies to an invalid manufacturer’s warranty, which was previously silently cleared, thereby deleting a properly maintained entry.
  • XML product import: now recognizes the six warranty and guarantee fields for the first time. If your feed does not include them, they remain untouched—a pure inventory or price feed does not change this.

v2.66.1

August 25, 2026
Deleted appointments disappear from the ticket export
  • The ticket export continued to include soft-deleted appointments. Anyone who cross-checked a deletion via the ticket assumed it had failed. The affected field was ` linked_appointments[] ` from ` GET /Export/JSON/tickets`.
  • The cause was an omission, not a typo. The block originated in v2.11.0—four months before soft-delete for events was introduced (v2.44.0)—and never had the filter applied when it was updated. A soft delete intentionally leaves the link line intact (undelete and CalDAV gravestone), so each reader must filter the data themselves. The ticket form and event widget in the backend have always done this—the API thus contradicted the same shop’s own user interface.
  • ⚠ Behavior change requiring no adjustment on your end: linked_appointments[] may become shorter, and entries may disappear between two requests without any changes to the ticket itself. There are no gravestones on this branch.
  • "Gravestones" still exist—and are more complete: GET /Export/JSON/appointments?ticket_id={id}&include_deleted=1 returns deleted_at and the descriptions in all languages; for batch retrieval, ?include_deleted=1&last_modified=… along with relationships[]. The tickets resource deliberately does not receive a include_deleted: the name would be ambiguous there because tickets do not support soft-delete.
  • The same issue was also fixed in the backend: eight queries linked projects, orders, and tickets via appointments without checking the appointment table. A deleted appointment continued to create a link, and the appointment links rendered from it led to nowhere.

v2.66.0

August 24, 2026
Import lock for the tax class
  • New lock type ` tax ` in ` import_blocks`. This was prompted by fixed book prices: the price lock alone does not preserve a fixed retail price—if the net price remains unchanged but the tax class changes during import, the gross pricestill changes, and that is precisely the fixed value. For price-fixed goods, therefore, always set this together with ` price `.
  • This type applies globally (regardless of language) and is evaluated by all product importers. It appears automatically during export because ` import_blocks[] ` is type-agnostic.
  • Related change in the backend: The product screen now has a single “Merchandise Management” tab instead of two tabs that controlled the same locks and deleted each other when saved.

v2.65.0

August 24, 2026
Server-side filters for the product list
  • Previously, only ?limit and ?offset were available. Anyone needing a subset had to retrieve the entire catalog page by page—with 62,000 items, that meant 250 retrievals for a query that the database answers in a single query.
  • New filters: ?status=, ?tax_class_id=, ?categories_id=, ?ean_prefix=, ?model_prefix=, and ?last_modified=. A comma separates an inclusion list (maximum of 100 values); multiple filters are combined with an “AND” operator. Example: ?ean_prefix=978,979,977&tax_class_id=0,1&status=1 finds all purchasable items with a book EAN that are not subject to the reduced tax rate.
  • The “not equal to” operator is intentionally omitted —the API doesnot support it for any entity. “Everything except tax class 2” is written as an inclusive list of the remaining classes; this remains indexable, rather than introducing a second query language.
  • stats.total now specifies the number of hits. Previously, this field always displayed the total number of products, regardless of the query— stats.has_more was therefore incorrect.
  • An invalid filter value results in an error (HTTP 400), not a warning. If the filter were silently discarded, the unfiltered catalog would be returned—a search for invalid records would then return “nothing found.” Filters applied to a single resource or an ID range are rejected for the same reason.
  • include_inactive_products Not applicable. The name belonged to a category’s product list, was never evaluated during product export, and has been replaced by ?status=. It now generates a warning instead of silently failing.
  • Schema: new index on products_last_modified (installer and self-healing; for very large catalogs, HealthCheck suggests it instead)—without it, the delta filter would have to perform a full table scan.

v2.64.0

August 24, 2026
Shipping addresses may remain empty
  • The address columns for an order now support NULL values (street, city, and ZIP code for each customer, shipping, and billing address). Two-phase marketplace imports create pickup orders even before payment is made—the address is not yet available at that point and is added during the second retrieval.
  • The export returns “ null ” for such orders instead of an empty string —“ null ” means “address not yet available,” while an empty string means “empty.” Company, address suffix, and state have always supported NULL values.
  • Correction: The flags for differing billing and shipping addresses now handle NULL values correctly. Previously, an empty field would incorrectly set them to “different.”

v2.63.0

August 17, 2026
Stock levels are readable; main warehouse stock fields are writable
  • Export nowprovides “ storages[] ”: The product export now includes inventory levels—for each warehouse row: storages_id, products_quantity, products_safe_quantity, products_reorder_level, products_storage_text, and options_id. Up to v2.62.0, the JSON export did not provide them at all: Inventory levels were writable via the API but not readable, making a round trip impossible. The field set corresponds exactly to the import, so exported inventory data can be re-imported unchanged.
  • If the extended inventory system is disabled, the key is missing entirely (not as an empty array)—this is how a client distinguishes between “feature disabled” and “no inventory lines available.” All inventory lines for the product are output, including those for inactive warehouses.
  • Main warehouse can be set via import: products_reorder_level and products_safe_quantity are now allowed at the top level of the product and write to the main warehouse (the products table). The export has always included both columns, but the import discarded them as unknown fields—so an exported product could not be re-imported without data loss.
  • Do not confuse this with the fields of the same name within storages[], which belong to an additional warehouse. storages_id: 1 is already the first additional warehouse—the main warehouse is not listed in the warehouse list.
  • Correction to products_quantity: Inventory is no longer rounded to whole numbers during import. The column allows for decimal places, and when the “Fractional Quantities in Shopping Cart” option is enabled, these are intended for specific purposes (metered goods, weight-based items)—previously, 0.5 would be converted to 0, meaning the item was marked as “out of stock.” Visible change for integrations that send decimal places: the value now arrives instead of disappearing silently.
  • Unchanged: storages[] remains a partial update without a full sync (v2.45.0); inventory lines not included in the update remain unchanged. CSV/XML imports and saving in the backend continue to write the complete inventory line.

v2.62.0

August 13, 2026
EU Warranty Label (GARAN) via Interface
  • New product fields: garan_label_enabled (0/1) enables the EU warranty label in accordance with DVO (EU) 2025/1960—mandatory as of September 27, 2026. To do this, use garan_brand (brand may differ from the catalog) and garan_model (model ID for the label); an empty string deletes the respective value. The model ID is approximately 14 characters long—longer values prevent the label from rendering.
  • The label is legally binding: Every approval via the interface is logged. The shop operator confirms the requirements, not the API.
  • Manufacturer’s warranty isnow in half-years: ` manufacturer_guarantee_years ` is a decimal value. Values with a comma ("2,5") are accepted and rounded down to the nearest half-year; they are never rounded up. Note for strict parsers: the export now returns ` "5.0" ` instead of ` "5"`.

v2.61.0

August 12, 2026
Retrieve Invoice Documents for an Order
  • New endpoint: GET /Export/JSON/order_document/{orders_id}?type=rg returns the completed document as JSON with a base64-encoded PDF. Available document types: of (quote), ab (order confirmation), rg/rgqr (invoice, optionally with a Swiss QR code), ls (delivery note), st (cancellation), pl (packing list), prg (pro forma invoice), dr (donation receipt), and so. Scope orders:read.
  • Why base64 instead of binary stream: The field is called “ content_base64 ”—just like with the ticket attachments from v2.60.0. This allows a receipt to be attached directly to a ticket without an intermediate file.
  • The document generated is exactly the same asthe one from the backend: The same renderer used for email attachments is run in a separate process and without a session. If an invoice file is stored for an order, it is delivered.
  • Clear error cases: unknown order → 404, invoice document without an invoice date → 409, invalid document type → 400 —each as clean JSON.
  • Not included: The eiv_* types (XRechnung, ZUGFeRD) are intentionally excluded—they generate XML instead of a PDF.

v2.60.0

August 11, 2026
Ticket attachments are now writable
  • New field attachments[]: POST and PUT /Import/JSON/tickets now accept files—for each entry, an name (filename including extension) and content_base64 (content Base64-encoded).
  • Previous behavior: Attachments were read-only (view and download since v2.9.0); they could only be uploaded via the backend form. A ticket created via the API therefore had no attachment—PDFs, .eml, or screenshots had to be submitted manually.
  • Attachments are linked to a history entry, not to the ticket: without an ` ticket_comments ` in the same request, no entry is created to which the file could be attached. It is then rejected with an error message instead of being silently discarded.
  • Same rules as in the backend: identical extension allowlist, same naming convention, and hash deduplication to prevent duplicate uploads. The actual file name assigned for each record is listed in the response under ` attachments`. The upper limit per file corresponds to the ` /media` endpoint (default 10 MB).
  • Error tolerance: an invalid extension, invalid Base64, or exceeding the size limit will generate an error per file —the ticket update itself remains successful. In the case of a public response with notification, the files are sent via the customer’s email.
  • Existing integrations: will continue to function without modification—the field is optional, and the extension is purely additive. Scope tickets:write.
Documentation corrections
  • Comment fields corrected: When creating a ticket, the Endpoint page listed the fields initial_comment and initial_comment_admin_id —neither of which exists in the API. The correct fields are ticket_comments (comment text) and admin_id. When updating, ticket_comments is also a text field, not an array of objects. Anyone who relied on the old description would not have received a comment—and consequently cannot add attachments either, because nothing can be attached without a history entry.
  • Public replies: The note “Direct customer replies are not possible via the API” was outdated as of v2.25.0 and has been replaced. The flags is_public_reply, notify_customer, and append_signature are now described in the field table.
  • No code changes: These two points affect only the documentation—the behavior of the API remains unchanged.

v2.59.0

August 6, 2026
Internal Extensions
  • No changes to the documented endpoints: This version exclusively extends an internal support interface of XONIC. Export, Import, and DELETE behave as before.
  • Existing integrations: will continue to run without modification—the version number simply increments.

v2.58.0

August 6, 2026
Combined Attribute Groups in Product Export
  • New field “ option_groups[] ”: The product export now includes attributes that are grouped together in the store (typically “Color + Size”), along with the group members in display order and, for each combination, the corresponding item number, EAN, quantity, price, and sort order.
  • Previous behavior: Grouped attributes were completely missing from the export—with no empty field and no notification. Only ungrouped attributes (options{}) were included.
  • Why two fields: For a characteristic group, the item number and EAN belong to the combination of values (“black / L” = one EAN); for a single characteristic, they belong to the individual value. options{} therefore remains unchanged—this is purely an additive extension.
  • Unchanged: The XML and CSV exporters continue to output only ungrouped attributes. The import continues to expect the flat options[] format.

v2.57.1

July 22, 2026
Bug fix: Customer-specific prices in the XML product import
  • XML product import: The ` customers` block within ` groups ` (customer-specific prices per product) was discarded during import and never reached the database—the entries are now imported correctly. Customer assignment works via id (shop customer ID) or external (WaWi customer number); unknown customer numbers are skipped.
  • Clear rules for incomplete entries: price 0 without tiered pricing does not create a line item (Remove = omit entry—the import fully reflects the customer price inventory for each product); missing quantity_blocks/min_quantity are preset to 1, as with the REST import.

v2.57.0

July 18, 2026
Customer Discounts & Custom Prices
  • Customers: New field discounts[] – Manage customer discounts (the “Discounts” field in customer management) via API. "replace-on-present" semantics: If the field contains entries, the customer’s existing discounts are completely replaced; an empty [] removes all discounts; if the field is missing, the discounts remain unchanged. The Customers export now includes discounts (round-trip).
  • Products Import: New field customers_prices[] – customer-specific prices, including tiered pricing, as a partial upsert (only columns included in the request are written). _action: "delete" removes a customer’s entry; customers_prices_replace: true switches to full synchronization; the customer is addressed via customers_id or customers_id_extern (WaWi customer number).
  • Customers Import: customers_id_extern and customers_group_id are now writable; PUT can also address the customer without an email via customers_id or customers_id_extern (purely for discount/master data maintenance).

v2.54.0

July 14, 2026

Orders: The date fields—especially the service date —can now be set on a per-item basis. This is required for consolidated invoices where each work package has its own service date (the documents then display it under the respective item line instead of just once in the document header). In addition, items can be appended to an existing purchase order via “ PUT /orders ” ( ) with “ products_mode: "append" ”; the totals are then recalculated. Purchase orders that are already considered invoices—because an invoice number has been assigned or the custom number range is disabled—will be rejected with the error “ ORDER_ALREADY_INVOICED ”: An issued invoice may not be modified retroactively. Status, tracking, and date updates remain unaffected.

v2.53.0

July 14, 2026

Orders: The date group in the order editor can now be set via API— service_date (service date), estimated_service_date, shipping_date, estimated_shipping_date, invoice_date (invoice date), invoice_payment_term_date (payment due date), and the requested date desired_delivery_date as free text. Applies to new entries (POST) and updates (PUT); date entries are parsed with some flexibility.

v2.52.0

July 14, 2026

Product Import: Using ` import_blocks `, you can specifically protect individual fields of an existing product from the XML importer —for example, if the German description comes from the ERP system but the English description is maintained in the shop. This is the programmatic counterpart to the backend tab “xoPort Data Interface (XML).” The name and description (language-specific) as well as seven price categories can be protected.

v2.51.0

July 11, 2026

Product Import: extra_fields now accepts additional fields either via the field ID or the field name —as an object or as a list, including the structure provided by the JSON export (export/import round trip). Previously, only the list with products_extra_fields_id was processed; differing spellings were discarded without notification. Unresolvable additional fields now generate a “ EXTRA_FIELD ” warning, and a field that is incorrectly placed at the top level instead of in “ extra_fields ” is named in the warning along with the corresponding field ID. This applies, among others, to the Google Shopping fields.

v2.50.0

June 30, 2026

Internal optimizations and stability improvements to the xoPort API.

v2.49.0

June 25, 2026

DELETE /customer_group/{id} (Scope customer_groups:delete): Deleting a customer group with two strict safeguards— Group 0 (“End Customers”) cannot be deleted (403), and a group with customers still assigned to it is rejected (409; regroup first). Clears all dependent data (discounts, specials, group prices, gating entries).

v2.48.0

June 25, 2026

New entity customer_groups (Export + Import): Enumerate and manage customer groups via API—including parsed restricted_categories_ids (the group’s category blacklist). POST/PUT with enum validation and ID hardcoding; POST requires an explicit customers_group_id (no AUTO_INCREMENT). Two new scopes: customer_groups:read + customer_groups:write.

v2.47.0

June 25, 2026

Category-Customer Group Block List: restricted_customer_groups now also available for categories (previously only for products). Import synchronizes customers_groups.restricted_categories; export returns the field. Recursive: If a parent category is blocked, all active subcategories are blocked as well.

v2.46.0

June 20, 2026

Media upload entity_type=static: new wildcard/deny list mode. With XOPORT_MEDIA_STATIC_FOLDERS = '*', all images/ subfolders are writable—except those managed by the image system (source/, thumbnail/). The default remains the strict allowlist; this mode is optional. Additional security measures: stricter folder name character set, path confinement under images/, and overwrite protection (existing files can only be overwritten with overwrite:true). New error codes: PROTECTED_TARGET_FOLDER (403) and FILE_EXISTS (409).

v2.45.0

June 19, 2026

Product Import storages[]: partial update. Per-warehouse inventory levels are now written only for the fields that are actually provided (INSERT … ON DUPLICATE KEY UPDATE instead of REPLACE). A partial payload such as {"storages_id":1,"products_reorder_level":50} thus changes only the reported inventory level and no longer sets the remaining inventory columns to 0. Full payloads remain backward compatible.

v2.44.0

June 18, 2026
Appointments API: Standalone Calendar Endpoint
  • New top-level resource appointments with full CRUD: GET /appointments, POST/PUT /appointments, and DELETE /appointment/{id} (soft delete → CalDAV tombstone).
  • Events are created using the central event logic (CalDAV-UID, updated_at) and immediately appear in the calendars of the responsible admins as well as in the CalDAV synchronization.
  • Fields include: startdate (required), enddate/deadline, allday/vacation/private/done, reminders, title/description/location (multilingual), responsibilities, followers, categories, and relationships (link to ticket, order, customer, or project).
  • Export filters: Time window, responsibility, link (e.g., ?ticket_id=), and ?last_modified= for delta sync. PUT receives fields that were not passed.
  • Three new scopes: appointments:read, appointments:write, appointments:delete.

v2.43.0

June 18, 2026
Tickets API: Create linked appointments
  • POST/PUT /tickets now accepts the optional field linked_appointments — appointments can be created directly for each ticket.
  • Each entry creates a calendar event and links it to the ticket. Assignees = the admins assigned to the ticket; Title Fallback = ticket subject.
  • This is the counterpart to the existing export field ` linked_appointments`; required scope: ` tickets:write`.

v2.42.0

June 15, 2026
Slider Entity Types & News Slider (Starting with Shop 4.8)
  • Starting with XONIC 4.8 (all versions after 4.7.15), the table ` slider ` uses the polymorphic column pair ` entity_type ` (`category ` / ` content ` / ` news`) + ` entity_id ` instead of ` categories_id ` + ` content_id`.
  • The slider payload (export & import) now uses entity_type + entity_id. Clients that are still sending categories_id/content_id must make the switch.
  • New type news: Slides can now also be attached to news articles. Homepage sliders = entity_type=content, entity_id=0.
  • Export filters ?categories_id= / ?content_id= are retained and map internally to the entity type. Existing stores are automatically migrated via Self-Heal.

v2.41.0

June 14, 2026
Media API: Project & Task Attachments
  • POST /media Using entity_type=project (or task), you can attach PDF documents directly to an xoCRM project or task.
  • The file is stored in the project’s internal, access-protected file area and appears immediately in the “Files” tab—ideal for internal documents such as project plans.
  • Required fields: entity_id (project or task ID, validated), file_name, and a source (file_url, file_base64, or multipart upload).
  • Currently, PDF files (application/pdf) are supported; required scope: media:write.

v2.40.0

June 11, 2026
Projects API: Status entry without status change
  • PUT /projects or /project_tasks with status_comment now creates an entry in the status history even without a status change—ideal for meeting minutes and work notes.
  • Task entries include the " progress_percent " — the progress bar appears in the history.

v2.39.2

June 11, 2026
Bug fix: One-time option costs in the frontend
  • When importing options with preselection, “ pre_option ” is now also set in the options screen—previously, the “plus” line for one-time option costs did not appear on the product page. “ pre_option ” is also allowed as an explicit field in the payload.
  • Core fix: The option price was lost if the option details table was completely empty.

v2.39.1

June 11, 2026
Bug fix: Product caches after import
  • After every product import, the product caches are updated (option preselections, price labels)—just as they are when saving in the backend.
  • Previously, attribute surcharges and price changes set via the API were not visible in the frontend until after a manual resave.

v2.39.0

June 11, 2026
Product Import: Attributes & Options (options[])
  • Product attributes can now be created via POST/PUT /Import/JSON/products: for each entry, use option_id or option_name + value_id or value_name — missing options/values are created automatically.
  • Control fields: option_type (e.g., 5 = display only), option_sort, option_required, price (surcharge), pre_select, status, model, ean, quantity, weight, image.
  • Combinations that have already been assigned are skipped (Warning OPTION_EXISTS_SKIPPED); options_replace: true completely rebuilds the product’s attributes.

v2.38.0

June 11, 2026
Products: Customer Group Block List
  • New field restricted_customer_groups[] in product export and import: Group IDs for which the product is blocked.
  • Import completely overrides the restriction list if the field is included in the payload; an empty array removes all restrictions.
  • This allows visibility restrictions (e.g., restricted to a specific customer group) to be transferred from one product to another via the API.

v2.37.0

June 11, 2026
Product Import: Click2Call & Direct Purchase Block
  • Two new importable product flags: click2call (Call button instead of shopping cart) and non_directcart (no direct purchase) — via POST/PUT /Import/JSON/products.
  • Behavior similar to non_cart/non_price: only set fields are modified.

v2.36.0

June 11, 2026
Projects API: Status Entries in Exports
  • Projects and tasks now provide status_history[] — all status entries maintained in the backend, including comments (e.g., meeting minutes).
  • Fields per entry: status_id + status_name, date_added, comments, progress_percent, user_id + user_name.
  • Purely additive, no new scope— projects:read or project_tasks:read is sufficient.

v2.35.0

June 11, 2026
Projects API: xoCRM Projects & Project Tasks
  • Two new entity types: projects (xoCRM projects) and project_tasks (project tasks)—each with Export, Import, and Delete operations.
  • Endpoints: GET /Export/JSON/projects (Single/Range/Filter, optionally ?include_tasks=1 with embedded tasks), POST/PUT /Import/JSON/projects, DELETE /Delete/JSON/project/{id} — similarly for project_tasks.
  • Six new scopes: projects:read/write/delete + project_tasks:read/write/delete.
  • Projects: multilingual descriptions[], categories, client assignment (customer_scope), project managers (leader_admin_ids), links to orders/tickets (relationships[]), is_private.
  • Tasks: Priority (1–5), Progress (0–100%), Due Date, Planned/Actual Hours, Assignment to a User (assigned_admin_ids).
  • Status Handling: Validation against status management, automatic status history entry upon creation and status changes (status_comment, admin_id).
  • Delete cascade: Deleting a project also removes all associated tasks; linked purchase orders/tickets are merely unlinked.

v2.34.0

June 10, 2026
Product Import: B-Grade Automation & New Fields
  • warranty_period: Warranty period can be set; an empty string "" overrides the default 24m.
  • products_xoport: Marker field (e.g., googleblacklist) can now be imported.
  • images[]: Assign additional product images by filename (replaces the existing gallery), including multilingual alt text; array or object format.
  • storages[]: Set stock levels per warehouse (multi-warehouse) — only when the multi-warehouse system is active; the total stock products_quantity remains separate.
  • downloads[]: Create product downloads (e.g., PDF); d_src links to a file at files/.
  • Media Upload: POST /media with entity_type=download uploads PDF/document files to files/.

v2.33.0

June 9, 2026
News Authors: Endpoint newsdesk_authors & author_id
  • New Endpoint /newsdesk_authors: Export news authors as a separate resource (GET /newsdesk_authors, GET /newsdesk_author/{id}) and write (POST/PUT /newsdesk_authors). Scope news:read/news:write.
  • Fields: language-neutral authors_name, authors_image, authors_email, authors_url, authors_company (company override for external authors), authors_since_year, authors_status, sort_order; per language authors_jobtitle and authors_bio.
  • author_id Link: The News Import/Export (newsdesks) now includes author_id —articles can be linked directly to an author.

v2.32.0

June 7, 2026
Orders Import: Checkout parity for tax lines & address details
  • Tax line per rate: ot_tax is now displayed in the frontend format with a rate-specific title for each tax rate — “plus 19% VAT” in net mode or “including 19% VAT” in gross mode (only if MODULE_ORDER_TOTAL_PREFIX=true) — instead of the previous static summary line “VAT:”. The amounts remain unchanged.
  • New address field additional_address: now included in the allowed address fields—the address suffix is automatically applied after customers_additional_address, delivery_additional_address, and billing_additional_address (no more UNKNOWN_FIELD warning).

v2.31.0

June 7, 2026
Orders Import recalc: Category & Customer Discounts Included in Price
  • The " recalc" mode now applies category/customer discounts (customers_discount + customers_groups_discount, including subcategory inheritance—the same logic as the frontend) to the item price in addition to group prices and specials.
  • The regular price is always discounted; the special price is discounted only when “ d_special=1 ” is set; the lower of the two effective prices takes precedence. As a result, the transferred order reflects exactly the price the customer pays at checkout.
  • Best-effort: If problems occur, the warning “ CATEGORY_DISCOUNT_UNAVAILABLE ” is displayed, and the calculation is performed without a discount. Assumption: one customer per request. Guest/marketplace orders (without “ customers_id ”) remain unchanged.

v2.30.0

June 7, 2026
Orders Import: Dropship Parity at Checkout
  • Show-only attributes: apply_default_attributes now also handles display attributes with preselection (Option Type 5, e.g., “Version”/“Protection Class”), including a signed surcharge—exactly as they appear in the checkout. Only internal hidden attributes (Type 6) remain excluded.
  • Cost Price & GTIN: In recalc mode, the cost price (products_ek_price from products_cost) and GTIN/EAN (products_ean) are loaded into the order line item if the caller does not provide them.
  • New flag compute_shipping (top-level, default false): Without an explicit totals.shipping, the cheapest shipping option is calculated “headless” via the frontend shipping stack (shipping::cheapest()) for the shipping address and written as ot_shipping (tax rate = dominant product tax rate). Best-effort: Errors do not throw exceptions but instead return the warning SHIPPING_AUTO_FAILED or SHIPPING_AUTO_EMPTY — the order is retained.
  • Use Case: Dropship transfer (e.g., SPV → SST), in which the resulting order should contain attributes, EK/GTIN, and shipping costs exactly as in a manual store order.

v2.29.0

June 7, 2026
Orders Import: Optional Sending of Order Confirmation
  • New flag “ send_confirmation_email ” (top-level, true): After the order is successfully created, the standard order confirmation is sent to the customer—exactly as with the normal checkout or the marketplace importers, including the copy recipients from SEND_EXTRA_ORDER_EMAILS_TO.
  • Recipients & Language: Email address from the created order (with validation), language from the payload or the order.
  • Feedback: The result is available as ` confirmation_email_sent ` (true/false) in the corresponding record of the response. Email errors do not abort the import.
  • Default unchanged: Without the flag, no email is sent (fully backward-compatible).

v2.28.0

June 7, 2026
Orders Import: Automatic Customer Field Filling, SKU Resolution & Automatic Default Attributes
  • Billing Address from Customer ID: If customers_id > 0 is set and billing is incomplete or missing, the customer’s default billing address (customer master + address book) is used—explicitly provided fields take precedence. delivery remains independent for different shipping addresses.
  • SKU Resolution: Items can be delivered based solely on products_model (= SKU) + products_quantity; products_id and products_name are supplemented from the store. Unknown SKU → Warning: PRODUCT_MODEL_NOT_FOUND.
  • Auto-Default Attributes: apply_default_attributes: true automatically transfers the preselected option values from the store—including surcharges—to the order for each item that lacks its own attributes.
  • Quantity calculation correction: In recalc mode, the net unit price is now saved correctly (previously doubled in the subtotal when quantity > 1).

v2.27.1

June 2, 2026
Tickets: Public response via API matches the backend tool
  • Author Persona: In “ is_public_reply=true ” mode, the history entry appears under the ticket persona of the admin handling the ticket (source: admin.ticket_settings) instead of under the login first/last name. Order: optional ticket_admin_id → admin.ticket_settings → TICKET_DEFAULT_ADMIN_ID → login name.
  • “Reply included”: The flag ticket_customer_notified_embedded is set when a public reply is sent via email—the reply text is included in the customer’s email.
  • CC/BCC: The CC/BCC recipients of the ticket are recorded in the history entry (badge visible) and included in the email.
  • Email Send Check: The return value of the email send is evaluated—a failed send now returns an error instead of a silent success.

v2.26.0

June 1, 2026
Orders Export: SQL-level pagination (memory fix)
  • Issue: Previously, GET /Export/JSON/orders loaded the entire order record (including line items, totals, and tracking) into memory and only then processed it via ?limit/?offset — on stores with a large number of orders, this led to a PHP fatal error “Allowed memory size exhausted, ” even when using ?limit=1.
  • Fix: LIMIT/OFFSET are now—similar to the /products endpoint—applied directly to the orders_id query. Only the requested page is loaded (default 50, max 250 via XOPORT_API_MAX_LIMIT).
  • Filter-aware: stats.total respects active filters (?status, ?customer_id, ?date_from/to, ?since, ?products_id, …); the response includes stats.total/limit/offset/has_more.
  • Unchanged: The single resource /order/{id} and the range /orders/{a}:{b} are intentionally left without paging (as with products).

v2.25.0

May 30, 2026
Tickets: Admin signature on public replies
  • Automatic signature: For is_public_reply: true, the PUT request to /Import/JSON/tickets now automatically appends the admin’s ticket signature (admin_settings.signatures.tickets) stored in the backend to the response text—both in the chat history entry and in the customer email, just like the backend response tool.
  • Language: The store’s default language; falls back to the first stored signature. If the admin has no signature, the text remains unchanged.
  • Opt-out: The optional flag ` append_signature: false ` (strictly boolean) suppresses the attachment.
  • Important for clients: Do not include your own signature in ` ticket_comments `—otherwise, it will appear twice.

v2.24.0

May 29, 2026
Tickets: Public Replies + Customer Notifications
  • New in the comment block of the PUT /Import/JSON/tickets request: two optional boolean flags per item— is_public_reply (true → history entry becomes public) and notify_customer (true → email sent to customer). Both are only effective when strictly === true; all other values are ignored.
  • Default behavior remains unchanged: without flags, the comment remains internal (ticket_internal_comment=1, no email) — backward compatible with all v2.11.0+ clients.
  • Email delivery uses the same $mail_ticket_update_with_content template as the backend tool (xpanel/tools-ticket-show.php), including CC/BCC from ticket_additional_data.receivers and attachments from the respective history line.
  • Security default: is_public_reply=true without a valid admin_id > 0 automatically falls back to internal + no email and returns a error. No new scope is required (tickets:write is sufficient).
  • Use Case: AI agents or external tools can now resolve tickets not only as internal notes but as actual customer responses—with a clear opt-in flag to make “accidentally public” practically impossible.

v2.23.0

May 29, 2026
Products Single-Resource Fix + API Consistency
  • Bug fix: GET /products/{id} now returns exactly one product. Previously, due to plural path routing, the range semantics WHERE products_id >= {id} took effect—the client would see a random product from the scan result via .data[0].
  • BREAKING CHANGE for Categories: The ?id= query format introduced in v2.1.0 at /categories?id={id} has been removed. Single-Resource is now available API-wide only in path form (/categories/{id}, /products/{id}). Reason: one canonical URL per resource, no overlap with bulk filters (?limit/?offset/?status/?language).
  • Unchanged: Range format /products/100:200 and bulk pagination (?limit=&offset=).
  • Note (pending): The same plural-path routing behavior exists structurally at suppliers/manufacturers/vouchers/customers/orders/newsdesks/cms/sliders — a separate ticket is planned.

v2.22.0

May 26, 2026
Slider API: Promotion Slides as a New Entity Type
  • New endpoints: GET /slider(s), POST/PUT /slider, DELETE /slider/{ID}.
  • Three new scopes: slider:read, slider:write, slider:delete (IDs 56–58).
  • Filters: status, type, categories_id, content_id, language.
  • Nested languages{} with title, description, direction, width, image, image_mobile, video, video_mobile.
  • Background: Until now, promo slides could only be managed via the backend. The new endpoint allows, for example, the correction of incorrect slide links after the fact via PUT /slider.

v2.21.0

May 19, 2026
Orders Import: Totals Modes, Auto-Tax & Drift Check
  • Auto-Derive from customers_group_id — Cascade Payload → Customer Record → 0 (Guest).
  • Auto-derive from tax_flag based on customers_groups.show_tax + customers_groups.tax_exempt (0=Net, 1=Gross, 2=Tax-Free). Explicit payload values take precedence.
  • Three totals modes (totals_mode): auto (Default, trust caller prices), recalc (reloadproducts_price + products_tax from the database, customer group and country-aware—ideal for marketplace imports with only SKU + quantity), verbatim (totals.total s are persisted 1:1, ideal for refunds and historical re-imports).
  • End-to-end drift check via expected_total + strict_totals. If deviation > 0.01 €: Default = Warning; with strict_totals: true = atomic rollback via OrderBuilderException('TOTAL_DRIFT', 422) before the first totals insert.
  • Whitelist validation for all Orders entities (orders, order_address, order_product, order_totals, order_external_reference) — unknown keys result in a deduplicated UNKNOWN_FIELD warning, similar to products/customers.
  • PHPUnit suite: 17 tests, 33 assertions (core/lib/classes/Tests/Order/OrderBuilderTest.php).

v2.20.0

May 19, 2026
Orders Endpoint & Generic Webhook
  • New endpoint POST/PUT /Import/JSON/orders with scope orders:write — create and update orders via API.
  • Generic service \\Xonic\\Order\\OrderBuilder (core/lib/classes/Order/OrderBuilder.php) — reusable for Marketplace and WaWi importers.
  • Typed exceptions via \\Xonic\\Order\\OrderBuilderException with errorCode, httpCode, and details.
  • Tax calculation: Supports gross (tax_flag=1) and net modes (tax_flag=0).
  • Inventory posting via xo_stock_change (best-effort: failures are returned as warnings ).
  • External Reference Tracking: Automatic entry in TABLE_ORDERS_EXTERNAL_REFERENCES based on an external ID.
  • send_webhook marketing response — generic outbound webhook with HMAC-SHA256 signature and enriched payload (product, order, customer, ticket).

v2.19.0

May 15, 2026
Media Endpoint: Static asset uploads (entity_type=static)
  • New mode entity_type=static in the media endpoint — enables the upload of branding assets (logos, interface images) without a database link.
  • Whitelist Configuration: Allowed destination folders via XOPORT_MEDIA_STATIC_FOLDERS (default: schnittstellen,marketplaces) — prevents path traversal attacks.
  • Required fields: entity_type, target_folder, file_name, and a source (file_url, file_base64, or multipart upload).
  • Default behavior: Overwrites existing files (overwrite=true). The response contains the direct image path.
  • Limitations: No database record, no thumbnails, no DELETE, no multilingual titles. A PUT request with ` static ` is rejected with ` STATIC_UPDATE_NOT_SUPPORTED `.
  • Use Case: Manage marketplace logos, interface branding, and static frontend assets via API.

v2.18.0

May 12, 2026
Products Import/Export: Upsells (Shopping Cart Add-ons)
  • New field ` upsells ` in the Products endpoint—similar to ` xsells`, but for “Goes Well With” suggestions in the shopping cart (table ` products_upsell`).
  • Export: For each product, an array containing products_model, products_id (source), and sort_order. A single-product export returns the upsells for the requested product (filtered by source PID).
  • Import via PUT: Accepts a simple array of model strings (["MODEL-A", "MODEL-B"]) or an array of objects ([{"products_model": "MODEL-A", "sort_order": 0}]).
  • Replace semantics: Existing upsells are completely deleted before the insert (DELETE-then-INSERT). Self-references and unknown models are silently skipped.
  • Use Case: Populating shopping cart add-on suggestions via API—configurable individually per product (no global default list).

v2.17.0

May 7, 2026
Products Import: Special Offers/Specials expanded
  • Complete Specials block: specials[] per product with customers_group_id, direct special price, or discount_percent.
  • Duration and Status: specials_begin, specials_end (alias expires_date), as well as status/special_status.
  • Offer names: specials_price_id or language-specific specials_price_name with automatic slp upsert.
  • Maintenance and deletion: Upsert via (products_id, customers_group_id); removal via action: delete or delete: 1. The old special block remains backward-compatible.

v2.16.0

April 29, 2026
Media API – /media endpoint fully implemented
  • Full CRUD support: Export (GET), Import (POST/PUT), and Delete (DELETE) for the product image library (products_images)—previously, all operations were 501 Not Implemented stubs.
  • Three upload modes: file_url (server download via cURL), file_base64 (inline), and multipart/form-data (field file). Choose based on the client and file size.
  • Validation: MIME sniff (image/jpeg|png|gif|webp), file size limit via XOPORT_MEDIA_MAX_FILESIZE (default 10 MB), automatic collision avoidance, filename sanitization.
  • Multi-language titles: descriptions[] with language_id or language_code per image. PUT with descriptions replaces everything (full sync semantics).
  • Main Image Promotion: An optional ` is_main=1 ` in a POST request also sets ` products.products_image`.
  • Thumbnails on demand: URLs for mini/small/medium/large/xlarge are returned in every response—physical generation occurs on-the-fly via image_thumb.php on the first call.
  • Safe DELETE: Removes the record, description lines, source file, and all thumbnails—but only if no other database reference points to the filename. Bulk delete via ?entity_id= or {ids:[…]}.
  • New scopes: media:read, media:write, media:delete.

v2.15.0

April 23, 2026
Products Import: Family/Variants Fields + SEO Collision Auto-Correction
  • Family/Variants fields in import: products_family (String tag), products_family_type (Integer, 3= Variant Master-Slave), and products_master (Integer, Master PID or Self-Reference) are now accepted—enabling Master-Slave groupings (e.g., package variants) via JSON import
  • Master-Slave Workflow: Create master → Retrieve PID from records[0].products_id → Second PUT with products_master = eigene PID for self-reference, then assign slaves
  • SEO Uniqueness Check: During import, seo_name (or the fallback products_name_as_seo) is checked against all other products—similar to the backend logic
  • Auto-correction: In case of a slug collision, the name is automatically corrected to <slug>-<PID> (via tep_search_new_seo())—the import continues success: true
  • Response warnings: Corrections are reported in warnings[] with type: "SEO_NAME_CORRECTED", including requested, corrected, and conflicting_with.{products_id, products_model, products_name}
  • Fixes a previously existing issue: The backend now issues warnings for slug collisions; previously, the API silently allowed them to pass through, resulting in products that could not be found

v2.14.0

April 22, 2026
Products Import: Short aliases for meta SEO fields
  • New short aliases: title_tag, desc_tag, keywords_tag are now accepted in the Products Import—consistent with the Categories API
  • Internal Mapping: The aliases are mapped to the database columns products_head_title_tag, products_head_desc_tag, and products_head_keywords_tag
  • Priority: Full column names take precedence if both variants are sent
  • Backward compatibility: Existing payloads with the long field names continue to work as is
  • Export: Continues to return the ` products_head_*_tag` fields (not the aliases)

v2.13.0

April 20, 2026
SEO History Auto-Redirects for all entity types
  • When names are changed via JSON import (PUT), seo_history entries are now automatically created for Products (p), Categories (c), Manufacturers (m), News (n), and CMS (s) —old URLs are redirected to the new ones via 301 redirects
  • Products: Fixed — now also works for renames WITHOUT an explicit seo_name (via products_name_as_seo auto-regeneration)
  • Manufacturers: Language-independent (one sh_history entry, one description line per language with the same slug)
  • News & CMS: language-specific (like Categories/Products)
  • Idempotent: the same rename payload does not create a second entry
  • Helper ` persistSeoHistory() ` + ` buildSeoHistoryDeletestamp() ` extracted into ` ImportJSON ` (DRY)

v2.12.0

April 20, 2026
Categories Product List (Lightweight Endpoint)
  • New sub-endpoint: GET /categories/productlist — Lightweight mapping of categories to product IDs and models
  • Query parameters: ?categories_id=, ?status=, ?include_inactive_products=, ?language=
  • Response: For each category, an array containing categories_id, categories_name, products_count, and products[], along with id + model
  • Memory-efficient: A single SQL query instead of 12+ relations
Products SQL-Level Pagination
  • Fix: GET /products now uses SQL-level pagination (LIMIT/OFFSET directly in the query)
  • Before: All products + relations loaded into memory, then array_slice()
  • After: Only the requested page is loaded from the database — no more memory limit
  • Individual retrieval via /products/{id} and ranges remains unchanged

v2.11.0

April 13, 2026

  • Tickets Full CRUD: Import (POST/PUT) and Delete endpoints for tickets. tickets:write and tickets:delete scopes are now active.
  • Export: linked_appointments[]: Linked CRM appointments are now included in the export for each ticket (appointment ID, start/end, title, description, location).
  • Import POST (Create): Required fields: ticket_subject, ticket_customers_email, ticket_customers_name. Generates ticket_link_id, creates an upload directory, optional initial comment + admin assignments.
  • Import PUT (Update): Change status/priority/department, add internal comments (ticket_internal_comment=1 always). admin_id is a required field for comments (0 = System).
  • Delete: Cascading deletion (status_history → admins → followers → appointments_relationships → CRM followers → files → ticket).
  • Scopes: tickets:write (Import/Update), tickets:delete (Delete) – previously marked as “planned,” now fully functional.

v2.10.0

April 9, 2026

  • Global pagination: All export endpoints support ?limit=n&offset=n. Default limit 50, hard cap 250 (configurable via XOPORT_API_MAX_LIMIT / XOPORT_API_DEFAULT_LIMIT). Response contains stats.count, stats.limit, stats.offset, stats.has_more.
  • Tickets – New Filters: ?admin_id=n for assigned tickets (INNER JOIN on ticket_to_admins) and ?created_by_admin_id=n for creator filters.
  • Memory Protection: Auto-pagination for large datasets prevents memory exhaustion.

v2.9.0

April 8, 2026

  • Tickets API (Export): New endpoints GET /tickets and GET /ticket/{id} for ticket export with complete conversation history, assigned admins, and followers.
  • Attachment endpoints: GET /ticket_attachments/{id} for metadata list (name, size, MIME type) and GET /ticket_attachment/{id}?file=name for binary download with path traversal protection.
  • Query filters: ?status=, ?priority=, ?department=, ?customer_id=, ?type=, ?date_from=&date_to=, ?last_modified=, ?language=
  • New scopes: tickets:read (implemented), tickets:write, tickets:delete (implemented in v2.11.0)
  • Metadata: ticket_statuses, ticket_priorities, ticket_departments in every response

v2.8.1

March 11, 2026

  • Bug fix: Categories last_modified: Now always set during PUT updates—even if only languages data (description, name, etc.) is updated. Previously, last_modified was only updated if main table fields (status, parent_id, etc.) were included in the request.
  • Newsletters ALLOWED_FIELDS: languages has been added to the list of allowed fields—no more UNKNOWN_FIELD warnings during multi-language newsletter imports.

v2.8.0

March 6, 2026

  • Newsletter Subscribers API: New endpoints for newsletter subscribers (Export/Import/Delete) with DOI support.
  • Newsletter Campaigns API: New endpoints for newsletter campaigns (Export/Import/Delete) with multi-language support.
  • New scopes: newsletter:read, newsletter:write, newsletter:delete, newsletters:read, newsletters:write, newsletters:delete
  • DOI Support: SHOP_NEWSLETTER_DOUBLE_OPT_IN configuration is honored; a confirmation email is sent when DOI is enabled.
  • Provider Sync: Automatic synchronization with external providers (Cleverreach, Mailchimp, Optimizely) via xoNewsletter.
  • GDPR Compliance: Privacy logging for import/delete operations; cascading deletions are documented.
  • Safety Checks: Locked campaigns are protected (409 LOCKED), and sent newsletters cannot be deleted (409 HAS_SENT_ENTRIES).
  • Subscriber Filters: ?status=confirmed|unconfirmed, ?email=, ?customer_id=, ?language_id=, ?last_modified=
  • Campaign filters: ?status=, ?language_id=
  • Bulk Delete: Multiple subscribers can be deleted via JSON body (by ID or email)

v2.7.0

February 25, 2026

  • Contracts API: New endpoints for contracts/subscriptions (Export/Import/Delete).
  • Status Endpoint: Dedicated PUT /contracts/{id}/status endpoint for status changes.
  • New scopes: contracts:read, contracts:write, contracts:delete, contracts:write
  • Query Filters: ?status=, ?customer_id=, ?product_id=, ?interval=, ?date_from=&date_to=, ?active=1
  • Nested Data: Attributes, Domains, Status History, Orders
  • Cascade Delete: Attributes → Domains → Order Links → Status History → Contract

v2.6.0

February 24, 2026

  • SEO History API: New endpoints for SEO redirects (Export/Import/Delete).
  • Auto-Redirect: When seo_name changes occur in the Product/Category JSON import, SEO history entries are automatically created (similar to XML import).
  • New Scopes: seohistory:read, seohistory:write, seohistory:delete
  • Query Filters: ?type=, ?base_id=, ?language=, ?expired=1
  • Bulk DELETE: Delete via JSON body or query filter

v2.5.0

February 23, 2026

  • Extended Product Fields: 19 new fields for contract and logistics data in Product Import.
  • Contract fields: contract, contract_use_current_price, contract_option — Contract products can now be created entirely via the API.
  • Logistics fields: products_cost, products_inventory_management, products_min_qty, products_max_qty, products_qty_blocks, products_base_price
  • Item Fields: products_sort_order, products_ean, products_free_shipping, products_image
  • Flags: service, sitemap, non_cart, non_price
  • Mappings: shipping_profile, products_unit_id, base_unit_id

v2.4.2

February 19, 2026

  • Product Import: Nested languages Format: Consistent with export and all other entities (Categories, Newsdesks, CMS). Accepts {"languages": {"de": {...}, "en": {...}}} with ISO codes or numeric IDs.
  • Auto-Fill on INSERT: Missing languages are automatically populated with master language data.
  • Flat format removed: products_name + language_id at the top level is no longer supported.
  • Symmetrical roundtrip: Export → Import → Export yields identical data.

v2.4.1

February 19, 2026

  • SEO Fields in Product Import: 7 new fields: products_head_title_tag, products_head_desc_tag, products_head_keywords_tag, seo_name, products_url, products_checkout_description, products_image_title
  • Enables a complete export→import round trip for all product description fields.

v2.4.0

February 6, 2026

  • CMS Endpoint: New entity type ` cms ` with full CRUD operations (Export/Import/Delete).
  • New scopes: cms:read, cms:write, cms:delete
  • Safety Checks: System pages (IDs 1–999) are protected; ` content_lock ` is respected
  • Query parameters: ?status=, ?module=, ?language=
  • Multi-language support with nested languages format

v2.3.0

January 31, 2026

  • Suppliers CRUD: New endpoints for suppliers (Export/Import/Delete)
  • Product Export expanded: videos, suppliers, contenteditor fields
  • Product Import expanded: categories_ids, groups, specials, extra_fields, badges, xsells, tabs, videos, suppliers, contenteditor
  • Manufacturer Import: All GPSR fields (EU + non-EU contact information)
  • New Scopes: suppliers:read/write/delete
  • Stubs: Marketing, Newsletter, Media Endpoints (501 Not Yet Implemented)

v2.2.0

January 27, 2026

  • Unknown Field Warnings: Response now includes an ` warnings ` array for unknown/ignored fields
  • Deduplicated: Each unknown field appears only once, even with 100+ records in a bulk import
  • Nested Fields: Also checks ` languages.* ` and ` addresses[] ` objects
  • Non-blocking: The request is processed successfully despite warnings (Postel’s Law)
  • Warning object: {type, field, entity, message, first_occurrence}

v2.1.0

January 27, 2026

  • RESTful HTTP method separation:
    • POST = INSERT only (create new records)
    • PUT = UPDATE only (update existing records)
  • Strict validation: POST with an existing ID → 409 Conflict, PUT with an unknown ID → 404 Not Found
  • New response field: ` records ` array with ` {index, entity_id, status} ` for each record
  • HTTP 405 Method Not Allowed if the wrong method is used (e.g., DELETE on the export endpoint)
  • All 8 entity endpoints support POST/PUT: categories, products, customers, manufacturers, newsdesks, newsdeskcats, faq, faqcats

v2.0.3

January 26, 2026

  • DELETE endpoints for newsdesks and newsdeskcats
  • Safety checks: Categories with subcategories or linked articles cannot be deleted
  • Error codes: HAS_SUBCATEGORIES, HAS_ARTICLES
  • Cache deletion after DELETE

v2.0.2

January 26, 2026

  • Newsdesk categories_ids in export and import
  • News articles are assigned categories (like products)
  • Cache clearing after newsdesks/newsdeskcats import
  • Newsdesks/Newsdeskcats import with nested languages format

v2.0.1

January 26, 2026

  • Categories import with nested languages format
  • Language keys can be used as code (de, en) or ID
  • OAuth2 Clients Admin UI with live search and sorting
  • Payload logging with request/response body

v2.0.0 Breaking

January 26, 2026

  • BREAKING: OAuth2 Client Credentials (RFC 6749)
  • API Keys Removed—Migration to OAuth2 Required
  • New endpoints: POST /oauth/token, GET /me
  • New tables: xoport_oauth_clients, xoport_oauth_tokens
  • Token prefix: xoat_, Client ID prefix: xoc_
  • Backend: Tools → xoPort OAuth2 Clients

v1.5.0

January 26, 2026

  • FAQ Endpoints (faq/faqcategories Export/Import/Delete)
  • /me Endpoint for Scope Introspection

v1.4.x

January 23, 2026

  • DELETE Endpoints for Categories, Products, Manufacturers, Customers
  • Safety checks to prevent accidental deletion
  • Bug fix: Fixed double escaping in HTML attributes

v1.0.0 - v1.3.x

January 23, 2026

  • Initial JSON API with export/import
  • Multi-address management for customers
  • Categories import with auto-seo_name
  • Language Filter, ISO-2 Country Codes
  • API Key Authentication (deprecated in v2.0)

Stay Up to Date

We’ll keep you informed about breaking changes via our newsletter.