API Change Log
API Changelog
An overview of all changes and improvements to the xoPort REST API.
Versions
v2.122.6 Current
October 6, 2026Export Product Reviews
- New:
GET /reviews,GET /review/{reviews_id}, and the sectionGET /reviews/{von}:{bis}with the scopeproducts: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, anddescriptions[]; per language withreviews_title,reviews_text, andreviews_reply(response from the store). - Filters:
products_id(also as a comma-separated list),status(1= approved, default;0= awaiting approval;all) andsince(created or last modified on or after). You can navigate through the list as with products usinglimitandpage. - No customer data:
authoris the name that also appears on the product page. The endpoint does not return the customer ID, email, or order number—onlyverified_purchase. As of Shop 4.9.153.
v2.122.5
October 1, 2026XML 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, 2026Features 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 /ticketsnow 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, 2026Creating Orders: Prices for Gross Calculation
orders:writewithtax_flag = 1: Prices determined by the shop (recalc) and shipping fromcompute_shippingare 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_totalto account for the total being too low will now receive the warningTOTAL_DRIFT(withstrict_totalsHTTP 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, 2026Customers: Partially change addresses without the country field reverting
PUT /Import/JSON/customersUsing `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, 2026Security: Customer and order exports without password data
- The exports
/customersand/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, andcustomers_hashhave 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, 2026News Import Creates Missing Languages
POST /Import/JSON/newsdeskWhen 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, 2026REST 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, 2026Tickets: No more false warnings for linked appointments
linked_appointmentsThe 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, 2026Tickets: Processing Claim
- New endpoint
PUT /Import/JSON/ticket_claims(scopetickets:write) shows who is currently working on a ticket. Required fields:ticket_id,admin_id, andstate(claimedorreleased); optional fields:ttl_minutes(default 60, 5 to 240),label, andforce. - Result per record in
records[].status:claimed,refreshed,taken_over,released,unchanged, orconflict. ⚠ A conflict is counted asfailed. GET /ticketsandGET /ticket/{id}return the new fieldsclaimandedit_lock(the ticket is currently open in the backend), each returningnullif 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_modifiedor 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, 2026XML 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, andwarnings.invalid_xml_chars_removed.files.processed.filenamesno 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, 2026Excluded Products on Coupons
GET /couponsandGET /coupon/{id}provide the new fieldrestrictions.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, 2026Coupons: restrict_mode corrected
- ⚠
restrict_modewas 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 nowdeny_strict= all except items that are in any listed category. - This field exclusively describes
restrictions.categories.restrictions.productsandrestrictions.manufacturersare 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, 2026Assign 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 returnsinvoice_id_individuellandinvoice_date. Otherwise,invoice_number_reasonspecifies the reason (mode_disabled,already_assigned,locked), accompanied by the warningINVOICE_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, 2026New categories with the specifications from the backend form
- ⚠ If you create a category without
sitemaporshop_ids, the same values now apply as in the backend form:sitemap = 1and 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, 2026Remove videos and tracking numbers
videos[]: Remove individual entries using_action: "delete", or usevideos_replace: trueto 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"viaid_trackingorservice_providerandcode. 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, 2026Attributes of an existing order line item
products_mode: "update"attributesnow 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, 2026Read and process CSV/XLS files
- New:
GET /Export/JSON/porters,/porter/{id}, andPUT /Import/JSON/porterswith the scopesporters:readandporters:write. activeSets the status (true= active thereafter) instead of toggling it. Withoutactive, the request is rejected. When deactivated, the shop removes the generated export file as in the backend.- The one-time query at
columnsreturns the complete column mapping for the Porter.
v2.108.0
September 13, 2026Read and Change Settings
- New:
GET /Export/JSON/configurationandPUT /Import/JSON/configurationwith the scopesconfiguration:readandconfiguration:write. - ⚠ Only keys that the administrator shares in
XOPORT_CONFIG_API_KEYSare 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_ACTIVEindicates a domain override that overrides the modified default value.
v2.107.0
September 13, 2026Processor in Order History
user_idfor an orderPUTenters the responsible agent into the history entry. Only an existing agent ID is accepted.customer_notifiedis still written, but the interface reports viaNO_MAIL_SENTthat it does not send a status email itself.
v2.106.0
September 13, 2026Upload product videos, option images, and subfolders
- Media upload via
entity_type: "product_video"(up to 100 MB) andentity_type: "option"for option images. subfolderSaves 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, 2026The 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, 2026Replaced 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_removedandreplaced_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, 2026Ticket Departments
- New:
GET /Export/JSON/ticket_departments(?active=),/ticket_department/{id},POST/PUT /Import/JSON/ticket_departments, andDELETE /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=1deletes them regardless.
v2.102.0
September 13, 2026Vouchers and Coupons as JSON
- New:
GET /Export/JSON/couponsand/coupon/{id}with the filters?code=,?type=,?active=,?kind=, and?valid_on=. - Restrictions are provided as ID lists, along with the readable fields
restrict_modeandis_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, 2026The 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, 2026Filter configuration for category pages
- New:
GET /Export/JSON/products_filtersand/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, 2026Customer export with filters and pagination
- New filters at
/customers:customers_id,customers_group_id,language_id,email,lastname,company,customers_id_extern, the prefix filtersemail_prefix,lastname_prefix,company_prefix, as well aspurchased_without_account,sort,order, andpage. - Large customer datasets are paginated in the database rather than loaded in their entirety.
- An invalid filter value results in
400 INVALID_FILTER_VALUEinstead of being silently ignored.
v2.98.0
September 12, 2026Tracking 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_mailis 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, 2026Remove ticket attachments, resend email
- New:
DELETE /Delete/JSON/ticket_attachment/{tshid}?filename=. notify_history_idSending toPUT /Import/JSON/ticketsresends the reply email for an existing history entry without creating a new one.apply_value_defaultsWhen 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, 2026Attributes and option values as separate endpoints
- New:
GET /Export/JSON/products_options,/products_option/{id},/products_options_values, andPOST/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_idin the product import is now rejected withOPTION_NOT_FOUND, instead of silently creating a characteristic.
v2.95.0
September 12, 2026Delete individual history entries
- New:
DELETE /Delete/JSON/order_status_history/{id}andDELETE /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, 2026Delivery Time Profiles
- New:
/shipping_profilesand/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, 2026Quick 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
PUTwithproducts_idcould result in an HTTP 500 error, and address changes did not trigger a redirect when performed this way.
v2.92.0
September 12, 2026Configurator 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, 2026Status history and document texts for each order
- The order export provides
status_history[]with all history entries. indiv_text0indiv_text4(replacement texts per document) as well as and (filenames of stored PDFs) are writable.indiv_rgindiv_ls- Warnings from an order
PUTnow appear next to the corresponding entry inrecords[].
v2.90.0
September 12, 2026Tax Classes and Ticket Details
- New:
GET /Export/JSON/tax_classesand/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, 2026Partial 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. outcomeReportspartialwhen part of the order was rejected. Also new is?order_id=sent to/tickets.
v2.88.0
September 12, 2026Multilingual Product Tabs
tabsis supported in the flat, language-nested, and export formats; the export provides one `languages` block per tab.- New categories without
sitemap/shop_idsare reported asCATEGORY_DEFAULTS_APPLIED.
v2.87.0
September 12, 2026Fees and order reference are writable
handling_fee,transaction_fee,selling_fee,other_fee, andpo_numbercan be set viaPOSTandPUT.- 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, 2026Pagination and Sorting
?sort=and `?order=` to `/orders` and `/products`; `?page=` has been implemented and is confirmed as `stats.page`.pageCombined withoffset, this results in an error. A truncatedlimitentry returnsLIMIT_CAPPED.
v2.85.0
September 12, 2026Special 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, 2026Category locks are writable
subs_blockand “tree_block” are writable. ⚠ “subs_block” affects redirects and the sitemap for the entire category branch.
v2.83.0
September 12, 2026All 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, 2026The manufacturer based on the name
manufacturers_nameand 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, 2026Special prices and additional fields can be returned
specialAccepted as a list or single object;slp_valueis used as the label name.extra_fieldsAppears as an empty list even without entries.
v2.80.0
September 12, 2026Restore 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, 2026Partially update categories
- A category
PUTno longer requires the language block. categories_mode: "append"Adds assignments to the product; the full sync reports removed assignments usingCATEGORY_ASSIGNMENTS_REPLACED.
v2.78.0
September 12, 2026Assign a ticket to a customer via PUT
ticket_customers_orders_id,ticket_customers_id,ticket_customers_name, andticket_customers_emailare written toPUT. The email address cannot be left blank.
v2.77.0
September 12, 2026All or Nothing During Import
?_strict=trueCompletely rejects products, categories, customers, and orders with unknown fields (400 UNKNOWN_FIELD) before anything is written.
v2.76.0
September 12, 2026Address 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, 2026Exact product filters
- New:
products_modelandproducts_eanas exact filters;products_idas a filter;manufacturers_idnow 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, 2026Tickets: 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, 2026Numbers 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, 2026Document retrieval without side effects
- Fixed: Retrieving an invoice correction via
order_documentcould generate a correction number. If no correction exists, the endpoint now responds with409. - Unused fields in an order (
PUT) return the responseFIELD_IGNORED_ON_UPDATE. Payment method, billing address, and shipping address can be corrected viaPUT.
v2.71.0
September 12, 2026Read and write attributes in full
- ⚠ Existing attribute assignments are updated rather than skipped; fields that were not sent remain unchanged.
options_replaceSynchronizes 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, 2026Deletion now requires the DELETE method
- ⚠ Please check your connection. The endpoints at
Delete/JSON/now accept only the HTTP methodDELETE. The API responds to any other method with405 METHOD_NOT_ALLOWEDand the header fieldAllow: 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 usesGETorPOST, 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) anderror.message.
v2.69.0
September 7, 2026Recipients in the CC/BCC fields are now editable
- New field “
receivers” atPOST/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, 2026Main 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 value0overrides 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, 2026Warranty 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_periodis 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, 2026Deleted 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=1returnsdeleted_atand the descriptions in all languages; for batch retrieval,?include_deleted=1&last_modified=…along withrelationships[]. The tickets resource deliberately does not receive ainclude_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, 2026Import 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, 2026Server-side filters for the product list
- Previously, only
?limitand?offsetwere 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=1finds 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.totalnow specifies the number of hits. Previously, this field always displayed the total number of products, regardless of the query—stats.has_morewas 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_productsNot 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, 2026Shipping 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, 2026Stock 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, andoptions_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_levelandproducts_safe_quantityare now allowed at the top level of the product and write to the main warehouse (theproductstable). 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: 1is 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.5would be converted to0, 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, 2026EU 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, usegaran_brand(brand may differ from the catalog) andgaran_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, 2026Retrieve Invoice Documents for an Order
- New endpoint:
GET /Export/JSON/order_document/{orders_id}?type=rgreturns 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), andso. Scopeorders: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, 2026Ticket attachments are now writable
- New field
attachments[]:POSTandPUT /Import/JSON/ticketsnow accept files—for each entry, anname(filename including extension) andcontent_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_commentandinitial_comment_admin_id—neither of which exists in the API. The correct fields areticket_comments(comment text) andadmin_id. When updating,ticket_commentsis 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, andappend_signatureare 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, 2026Internal 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, 2026Combined 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, 2026Bug 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 viaid(shop customer ID) orexternal(WaWi customer number); unknown customer numbers are skipped. - Clear rules for incomplete entries:
price 0without tiered pricing does not create a line item (Remove = omit entry—the import fully reflects the customer price inventory for each product); missingquantity_blocks/min_quantityare preset to 1, as with the REST import.
v2.57.0
July 18, 2026Customer 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 includesdiscounts(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: trueswitches to full synchronization; the customer is addressed viacustomers_idorcustomers_id_extern(WaWi customer number). - Customers Import:
customers_id_externandcustomers_group_idare now writable;PUTcan also address the customer without an email viacustomers_idorcustomers_id_extern(purely for discount/master data maintenance).
v2.54.0
July 14, 2026Orders: 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, 2026Orders: 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, 2026Product 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, 2026Product 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, 2026Internal optimizations and stability improvements to the xoPort API.
v2.49.0
June 25, 2026DELETE /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, 2026New 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, 2026Category-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, 2026Media 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, 2026Product 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, 2026Appointments API: Standalone Calendar Endpoint
- New top-level resource
appointmentswith full CRUD:GET /appointments,POST/PUT /appointments, andDELETE /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, andrelationships(link to ticket, order, customer, or project). - Export filters: Time window, responsibility, link (e.g.,
?ticket_id=), and?last_modified=for delta sync.PUTreceives fields that were not passed. - Three new scopes:
appointments:read,appointments:write,appointments:delete.
v2.43.0
June 18, 2026Tickets API: Create linked appointments
POST/PUT /ticketsnow accepts the optional fieldlinked_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, 2026Slider 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 sendingcategories_id/content_idmust 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, 2026Media API: Project & Task Attachments
POST /mediaUsingentity_type=project(ortask), 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, 2026Projects API: Status entry without status change
PUT /projectsor/project_taskswithstatus_commentnow 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, 2026Bug 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, 2026Bug 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, 2026Product Import: Attributes & Options (options[])
- Product attributes can now be created via
POST/PUT /Import/JSON/products: for each entry, useoption_idoroption_name+value_idorvalue_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: truecompletely rebuilds the product’s attributes.
v2.38.0
June 11, 2026Products: 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, 2026Product Import: Click2Call & Direct Purchase Block
- Two new importable product flags:
click2call(Call button instead of shopping cart) andnon_directcart(no direct purchase) — viaPOST/PUT /Import/JSON/products. - Behavior similar to
non_cart/non_price: only set fields are modified.
v2.36.0
June 11, 2026Projects 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:readorproject_tasks:readis sufficient.
v2.35.0
June 11, 2026Projects API: xoCRM Projects & Project Tasks
- Two new entity types:
projects(xoCRM projects) andproject_tasks(project tasks)—each with Export, Import, and Delete operations. - Endpoints:
GET /Export/JSON/projects(Single/Range/Filter, optionally?include_tasks=1with embedded tasks),POST/PUT /Import/JSON/projects,DELETE /Delete/JSON/project/{id}— similarly forproject_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, 2026Product Import: B-Grade Automation & New Fields
warranty_period: Warranty period can be set; an empty string""overrides the default24m.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 stockproducts_quantityremains separate.downloads[]: Create product downloads (e.g., PDF);d_srclinks to a file atfiles/.- Media Upload:
POST /mediawithentity_type=downloaduploads PDF/document files tofiles/.
v2.33.0
June 9, 2026News 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). Scopenews: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 languageauthors_jobtitleandauthors_bio. -
author_idLink: The News Import/Export (newsdesks) now includesauthor_id—articles can be linked directly to an author.
v2.32.0
June 7, 2026Orders Import: Checkout parity for tax lines & address details
- Tax line per rate:
ot_taxis 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 ifMODULE_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 aftercustomers_additional_address,delivery_additional_address, andbilling_additional_address(no moreUNKNOWN_FIELDwarning).
v2.31.0
June 7, 2026Orders 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, 2026Orders Import: Dropship Parity at Checkout
- Show-only attributes:
apply_default_attributesnow 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
recalcmode, the cost price (products_ek_pricefromproducts_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, defaultfalse): Without an explicittotals.shipping, the cheapest shipping option is calculated “headless” via the frontend shipping stack (shipping::cheapest()) for the shipping address and written asot_shipping(tax rate = dominant product tax rate). Best-effort: Errors do not throw exceptions but instead return the warningSHIPPING_AUTO_FAILEDorSHIPPING_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, 2026Orders 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 fromSEND_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, 2026Orders Import: Automatic Customer Field Filling, SKU Resolution & Automatic Default Attributes
- Billing Address from Customer ID: If
customers_id > 0is set andbillingis incomplete or missing, the customer’s default billing address (customer master + address book) is used—explicitly provided fields take precedence.deliveryremains independent for different shipping addresses. - SKU Resolution: Items can be delivered based solely on
products_model(= SKU) +products_quantity;products_idandproducts_nameare supplemented from the store. Unknown SKU → Warning:PRODUCT_MODEL_NOT_FOUND. - Auto-Default Attributes:
apply_default_attributes: trueautomatically 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
recalcmode, the net unit price is now saved correctly (previously doubled in the subtotal when quantity > 1).
v2.27.1
June 2, 2026Tickets: 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: optionalticket_admin_id→admin.ticket_settings→TICKET_DEFAULT_ADMIN_ID→ login name. - “Reply included”: The flag
ticket_customer_notified_embeddedis 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, 2026Orders Export: SQL-level pagination (memory fix)
- Issue: Previously,
GET /Export/JSON/ordersloaded 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/OFFSETare now—similar to the/productsendpoint—applied directly to theorders_idquery. Only the requested page is loaded (default 50, max 250 viaXOPORT_API_MAX_LIMIT). - Filter-aware:
stats.totalrespects active filters (?status,?customer_id,?date_from/to,?since,?products_id, …); the response includesstats.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, 2026Tickets: Admin signature on public replies
- Automatic signature: For
is_public_reply: true, the PUT request to/Import/JSON/ticketsnow 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, 2026Tickets: Public Replies + Customer Notifications
- New in the comment block of the PUT
/Import/JSON/ticketsrequest: two optional boolean flags per item—is_public_reply(true → history entry becomes public) andnotify_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_contenttemplate as the backend tool (xpanel/tools-ticket-show.php), including CC/BCC fromticket_additional_data.receiversand attachments from the respective history line. - Security default:
is_public_reply=truewithout a validadmin_id > 0automatically falls back to internal + no email and returns aerror. No new scope is required (tickets:writeis 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, 2026Products Single-Resource Fix + API Consistency
- Bug fix:
GET /products/{id}now returns exactly one product. Previously, due to plural path routing, the range semanticsWHERE 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:200and 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, 2026Slider 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{}withtitle,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, 2026Orders Import: Totals Modes, Auto-Tax & Drift Check
- Auto-Derive from
customers_group_id— Cascade Payload → Customer Record → 0 (Guest). - Auto-derive from
tax_flagbased oncustomers_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_taxfrom the database, customer group and country-aware—ideal for marketplace imports with only SKU + quantity),verbatim(totals.totals 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; withstrict_totals: true= atomic rollback viaOrderBuilderException('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 deduplicatedUNKNOWN_FIELDwarning, similar to products/customers. - PHPUnit suite: 17 tests, 33 assertions (
core/lib/classes/Tests/Order/OrderBuilderTest.php).
v2.20.0
May 19, 2026Orders Endpoint & Generic Webhook
- New endpoint
POST/PUT /Import/JSON/orderswith scopeorders: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\\OrderBuilderExceptionwitherrorCode,httpCode, anddetails. - Tax calculation: Supports gross (
tax_flag=1) and net modes (tax_flag=0). - Inventory posting via
xo_stock_change(best-effort: failures are returned aswarnings). - External Reference Tracking: Automatic entry in
TABLE_ORDERS_EXTERNAL_REFERENCESbased on an external ID. -
send_webhookmarketing response — generic outbound webhook with HMAC-SHA256 signature and enriched payload (product, order, customer, ticket).
v2.19.0
May 15, 2026Media Endpoint: Static asset uploads (entity_type=static)
- New mode
entity_type=staticin 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, 2026Products 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), andsort_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, 2026Products Import: Special Offers/Specials expanded
- Complete Specials block:
specials[]per product withcustomers_group_id, direct special price, ordiscount_percent. - Duration and Status:
specials_begin,specials_end(aliasexpires_date), as well asstatus/special_status. - Offer names:
specials_price_idor language-specificspecials_price_namewith automaticslpupsert. - Maintenance and deletion: Upsert via
(products_id, customers_group_id); removal viaaction: deleteordelete: 1. The oldspecialblock remains backward-compatible.
v2.16.0
April 29, 2026Media 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 were501 Not Implementedstubs. - Three upload modes:
file_url(server download via cURL),file_base64(inline), andmultipart/form-data(fieldfile). Choose based on the client and file size. - Validation: MIME sniff (
image/jpeg|png|gif|webp), file size limit viaXOPORT_MEDIA_MAX_FILESIZE(default 10 MB), automatic collision avoidance, filename sanitization. - Multi-language titles:
descriptions[]withlanguage_idorlanguage_codeper image. PUT withdescriptionsreplaces 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.phpon 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, 2026Products 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), andproducts_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 withproducts_master = eigene PIDfor self-reference, then assign slaves - SEO Uniqueness Check: During import,
seo_name(or the fallbackproducts_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>(viatep_search_new_seo())—the import continuessuccess: true - Response warnings: Corrections are reported in
warnings[]withtype: "SEO_NAME_CORRECTED", includingrequested,corrected, andconflicting_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, 2026Products Import: Short aliases for meta SEO fields
- New short aliases:
title_tag,desc_tag,keywords_tagare 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, andproducts_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, 2026SEO History Auto-Redirects for all entity types
- When names are changed via JSON import (PUT),
seo_historyentries 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(viaproducts_name_as_seoauto-regeneration) - Manufacturers: Language-independent (one
sh_historyentry, 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, 2026Categories 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, andproducts[], along withid+model - Memory-efficient: A single SQL query instead of 12+ relations
Products SQL-Level Pagination
- Fix:
GET /productsnow 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:writeandtickets:deletescopes 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. Generatesticket_link_id, creates an upload directory, optional initial comment + admin assignments. - Import PUT (Update): Change status/priority/department, add internal comments (
ticket_internal_comment=1always).admin_idis 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 viaXOPORT_API_MAX_LIMIT/XOPORT_API_DEFAULT_LIMIT). Response containsstats.count,stats.limit,stats.offset,stats.has_more. - Tickets – New Filters:
?admin_id=nfor assigned tickets (INNER JOIN on ticket_to_admins) and?created_by_admin_id=nfor creator filters. - Memory Protection: Auto-pagination for large datasets prevents memory exhaustion.
v2.9.0
April 8, 2026
- Tickets API (Export): New endpoints
GET /ticketsandGET /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) andGET /ticket_attachment/{id}?file=namefor 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_departmentsin every response
v2.8.1
March 11, 2026
- Bug fix: Categories
last_modified: Now always set during PUT updates—even if onlylanguagesdata (description, name, etc.) is updated. Previously,last_modifiedwas only updated if main table fields (status, parent_id, etc.) were included in the request. - Newsletters
ALLOWED_FIELDS:languageshas been added to the list of allowed fields—no moreUNKNOWN_FIELDwarnings 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_INconfiguration 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}/statusendpoint 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
languagesFormat: 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_idat 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
languagesformat
v2.3.0
January 31, 2026
- Suppliers CRUD: New endpoints for suppliers (Export/Import/Delete)
- Product Export expanded:
videos,suppliers,contenteditorfields - 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_idsin export and import - News articles are assigned categories (like products)
- Cache clearing after newsdesks/newsdeskcats import
- Newsdesks/Newsdeskcats import with nested
languagesformat
v2.0.1
January 26, 2026
- Categories import with nested
languagesformat - 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)
/meEndpoint 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.