Import product videos in bulk over REST
A new REST endpoint imports product videos in bulk, mirroring the existing bulk image import. This makes it easy to attach many video links, such as YouTube URLs, to products at once.
All platform changes in one place. Filter by component or type to find what matters to you.
A new REST endpoint imports product videos in bulk, mirroring the existing bulk image import. This makes it easy to attach many video links, such as YouTube URLs, to products at once.
Querying a machine's spare part products could return an empty list even when the machine had parts. The parts are now returned as expected.
You can now search value set items by their localized description text, as a case-insensitive partial match across all languages, instead of only by the value code. Special characters such as %, _ and \ in a search term are matched literally.
On the REST product search endpoint, passing expands=categories had no effect. The parameter now correctly includes category information in the results.
Fixed the JSON-LD structured data for products and clusters, correcting the type, the fallback image, the price formatting and the availability markup.
Creating or updating a product attribute value did not always update the matching search facet filter, so faceted search could show stale values. Attribute changes now appear in the facets in near-realtime, without a manual reindex.
The order detail page now shows each item's shipment status as a translated, color-coded badge, with a status column in the item overview and Unknown shown when a status is missing.
Assigning users or companies to an order list, or adding items to it, through the REST order list endpoints could fail with a server error. These endpoints now work as documented.
The ICS surcharge flag is now added to a cart address only for logged-in contacts, not for guests or other user types.
You can now search companies, contacts and customers by a free-text term. An empty term is handled gracefully instead of returning an error.
Quick order prices are now calculated from the logged-in customer's pricing instead of a fixed tax zone, so the listed prices match the customer.
The GraphQL query for companies has been optimized. Fetching large pages of companies together with their contacts, addresses, sources and attributes is now significantly faster.
Querying attributes on a cluster product could fail the entire query with a server error when an attribute had no value. The query now succeeds instead of erroring.
The generated XML product sitemap now includes only products within the configured catalog root.
When an order moved from draft to new, the address code in the REST order response could be truncated, which could break downstream exports. Address codes are now returned in full throughout the order lifecycle.
The validateOrderList flag on the tender item inputs (TenderAddItemsInput and TenderUpdateItemInput) is deprecated. It has no effect, because order lists are not enforced when adding or updating tender items regardless of the flag.
Product and cluster detail pages no longer return a 404 for logged-in B2B users, because the queries now include the user's company and contact context.
The bulk image import endpoint can now match products by their external source and source id (productSource and productSourceId), so you no longer need to resolve an internal product id first.
The GraphQL schema descriptions for the net and gross price fields were reversed relative to the values the API returns. The descriptions now match the API, so the reference describes each field accurately. The returned values themselves are unchanged.
A regression made language codes on localized fields such as product names, short names, package descriptions and slugs case-sensitive, so mixing lowercase and uppercase codes could produce empty values on import. Language codes are treated case-insensitively again, so nl and NL are equivalent across stored data, display and API responses.
Searching for a decimal value such as 130.1731005 could fail, and is now fixed. Single-character words and numbers in a search term are ignored and do not refine the search, because the minimum length for a search token is two letters or numbers.
Adding a cluster combination to the cart now submits the correct bundle id, so the item is added successfully.
Adding or updating a cart item no longer errors when the backend returns an empty response.
When a login is rejected because the account must set a new password, the storefront now shows reset instructions and automatically triggers a password-reset email instead of a generic login error.
Removing a contact from a company and then adding it back could fail because the earlier link was retained internally. Contacts can now be removed from and re-added to a company reliably.
Importing categories could fail silently when the description or short description contained HTML or was very long. These descriptions now import correctly.
The bulk shipments endpoint now upserts on the source and sourceId combination. A shipment with a known combination is updated and a new one is created when it does not exist yet.
Updating a product through the bulk upsert by source id did not save changes to eanCode, package and packageUnit. These fields are now updated as expected.
Searching orders through the API could return incorrect or incomplete results for some filter combinations. Order search now returns the orders that match the query.
Changing the supplier on an order item while editing a quote or order was lost once the order was processed. The updated supplier is now stored on the order.
Media items returned incorrect values for their created and last modified timestamps. These now reflect the actual creation and modification times.
Bonus item discounts were not always applied correctly. Incentive results are now calculated and stored consistently on the order.
The orders and quote queries now return the name of the company an order belongs to. This is the company itself, not the company name stored on the order address, which can differ.
Creating a category with a parent that cannot be found returned a confusing message with a second, unrelated line about root categories. The endpoint now returns a single clear message that names the missing parent.
Sending productSource as an array on the bulk prices endpoint failed with a not-found error, while a single object worked. The endpoint now accepts productSource as an array as well.
Bulk cost prices now support a from and to validity date, the same way bulk prices do. Use them to schedule a cost price change ahead of time.
The bulk import for companies, contacts and customers now accepts attributes in the same nested payload. You can set attribute values during the import instead of sending a separate request.
Creating an attribute description no longer requires a default value. Leave it out for attributes that do not need one. This applies to both the GraphQL and REST APIs.
The combo deals section on a product page no longer errors when the product has no bundles.
Clusters (product variants) have options, the extra products you offer alongside the configured cluster product. A product can now be linked to any number of clusters as an option instead of exactly one, which helps when the same product fits several clusters. The same product can still be sold on its own alongside those cluster offerings.
The product, category and cluster endpoints (including cluster configs, cluster options and cluster products) now have stricter, more consistent validation across REST and GraphQL, with clearer error messages. Lookup keys are case sensitive (use sourceId, not sourceid), and bulk requests reject duplicate identifiers and empty arrays, with each batch rolled back if an item fails validation. Categories, products and clusters created without a parent are kept as root or floating items rather than placed under a default category.
The price fields that were deprecated earlier have now been removed from the product and cluster-product endpoints (price, taxCode, taxPercentage, costPrice, suggestedPrice, discountPrice, discountPriceFrom, discountPriceTo, costprices, surcharges and priceDisplay); use the pricing endpoints instead. Cluster options also have their own dedicated input and response types.
The pricesheet CSV import treated localized name and description columns with the wrong type, causing valid rows to fail mapping. The import now accepts these columns as expected.
You can now set the order of categories, and of the products inside a category, per storefront. Categories and individual products can also be hidden without unpublishing them, which keeps seasonal or less relevant items out of view while preserving their data.
Favorite list mutations accepted both productIds and productSources (or clusterIds and clusterSources) in the same call, which led to ambiguous behavior. These combinations now return a validation error.
Channels now support two new settings, defaultAppLanguage and defaultDataLanguage. These give a per-channel fallback when a request does not specify a language, so regional channels serve the correct locale by default without every client call passing an explicit language.
If you run multiple sales channels, for example a B2B portal alongside a spare-parts webshop, each channel can now have its own dedicated catalog. Products and categories are fully separated per channel, so a product surfaces only in the channels it belongs to and nothing appears in the wrong storefront.
attributeResultByClusterId could return a response.page that did not match the requested page and exceeded response.pages, breaking pagination logic built on that metadata. The returned page now matches the page actually served and stays within bounds.
The product specifications tab no longer stays blurred after loading, and it can no longer be loaded twice while a request is in progress.
The add-to-basket modal now shows the correct no-image placeholder when a product or bundle has no image.
Order details no longer break when a bonus product is missing. Such items are skipped or shown with a placeholder image and plain name.
Categories, products and clusters whose category path falls outside the configured catalog root now return a 404 instead of rendering.
Active price and range filters now show removable chips, the slider remembers widened bounds and resets correctly when a filter is removed, and the range filter type and applied values are preserved.
CategoryProductSearchInput now accepts applyOrderlists: Boolean and orderlistIds: [Int!]. Category queries can be scoped to one or more orderlists, so browsing a category returns only the products that belong to the selected orderlists. This makes it easier to render category pages that respect a customer's assigned orderlists.
Media update mutations did not remove a language entry from localized arrays such as name, description, alt text, image and video when that language was left out of the input. The existing value stayed in place instead of the array being replaced. Update mutations now replace localized arrays with the provided input, so dropping a language entry from the call removes it from the stored value.
A new propeller_allowed_countries filter lets a shop restrict which countries are accepted in cart delivery and invoice addresses, address forms and registration. A rejected submission, such as a disallowed country, now shows an error and stops instead of a success message.
In rare cases, a pricesheet discount that targets a category could apply to a product outside that category, returning a lower customer price than the price sheet defined. Category discounts now apply only to products that are actually in the targeted category.
Bulk inventory updates now succeed or fail as a whole, so a single bad record no longer leaves the rest of the update in an unknown state. Inventory is also cleaned up when its parent is removed: deleting a company removes its inventory, and deleting a warehouse unlinks it from any inventory that referenced it.
Cart queries now include logo URLs for payment methods and carriers. The payment method logo field was previously missing from the response and the carrier logo field returned null, so frontends can drop any local asset map keyed by method or carrier name and render the logos straight from the cart response.
Inventory records now support a companyId field, making it easier to reserve stock for a specific company in multi-tenant setups. This release also includes further performance improvements across inventory queries and mutations.
The sticky sidebar on a cluster page now positions correctly, pinning short sidebars below the header and tall ones to the bottom of the viewport.
Validation errors on REST endpoints now return more detailed information in the response body, making it easier to identify which field failed validation and why.
Business rules can now evaluate attributes on companies, contacts and customers when determining payment methods, carriers and incentives. This works the same way as order item attribute conditions and applies to both tender and cart evaluation. Company, contact and customer data is now passed through during rule evaluation so attribute-based conditions resolve correctly at checkout.
Partners and Backoffice users can now create and manage both GraphQL and REST API keys directly from the platform or through GraphQL. A dedicated user role has been introduced for API key access control.
The order query was returning an empty shipments array for authenticated users in certain cases. Shipment data is now consistently returned when querying orders.
Using negative values for page or offset when retrieving attribute descriptions returned an internal server error instead of a proper validation error. The API now returns a 400 response with a clear message.
Searching for orders by recipient first name, city, postal code or other delivery address fields was returning zero results. Only email-based searches produced matches. Order search now correctly matches against all supported search fields.
Importing attribute values via CSV for multiple entities (companies, products, contacts) was only updating the first entity in the file. The import logic has been corrected so that the same attribute can be set on multiple entities in a single CSV import.
Fixed an error that could break the display of spare parts on a product.
Fixed the products query returning incorrect results when filtering with class: PRODUCT inside a category. Cluster products were incorrectly indexed with category IDs from their parent cluster, causing them to appear in unrelated category results.
Fixed an error when setting keywords on products within clusters via the REST API. The clusterProductUpdate operation previously returned a 500 Internal Server Error when keywords were included in the update payload.
At checkout, a logged-in purchaser whose cart requires authorization and exceeds their limit is now redirected back to the cart instead of continuing.
The platform now supports embedding external workflows and AI agents directly in the Sales Hub and Backoffice. Agents appear inline on quotes, orders, the PIM and other pages. They are triggered by a button click or chat input and execute against external webhook endpoints.

How it works. Each agent is configured with a webhook URL. When triggered, Propeller sends a POST request containing the current page context (order data, company details, product list) and the user's message. The external endpoint processes the request and returns a response that is displayed inline. Any system that can handle HTTP POST and return JSON works: n8n, custom APIs, LLM services.
What this enables. Partners can build and connect their own logic: margin validation on quotes, reorder suggestions based on order history, conversational assistants for pricing or product selection, automated churn detection. The full commercial dataset (customers, orders, products, prices) is available in the webhook payload. Business rules like discount limits and catalog restrictions apply to agent interactions the same way they apply to manual actions.
Agent types control where the agent appears. Built-in types include SALES_HUB_ORDER_EDITOR, SALES_HUB_QUOTE_EDITOR and SALES_HUB_COMPANY. Custom types can be registered for frontend pages like product detail or customer portal.
GraphQL API. Agents are managed through agentCreate, agentUpdate and agentDelete mutations. Conversations use agentConversationCreate and agentMessageCreate. The agents query lists available agents filtered by type and trigger.
See the full documentation at Workflows and AI Agents.
A category page now resolves by its slug instead of falling back to the base catalog category when no category id is supplied.
In list view the add-to-basket button now shows only its icon on small screens, and the quote overview no longer shows a stray dash before the payment method and shipping amounts.
Fixed an issue where bulk import endpoints for companies, contacts and customers would detect false changes when email addresses contained mixed case characters. The comparison now correctly accounts for case sensitivity, preventing unnecessary updates.
Fixed an issue where addresses and attributes provided as nested payload in the company, contact and customer bulk endpoints could be incorrectly assigned across records in the same batch.
The uploadImage input property on the mediaImageCreate mutation now accepts an array, allowing multiple files to be uploaded in a single request. The same change applies to other media type mutations.
At the first checkout step, a logged-in user with an empty invoice address is now prompted to fill it in instead of seeing the Continue button.
Replaced the account details page with a dashboard layout featuring recent orders, quotes and a customizable content block.
Added support for secondary sorting options in the catalog and relevance-based sorting for search results with configurable field boosting.
Redesigned the catalog search results page to use a row-based list layout, better suited for B2B catalogs with technical products.
Migrated product short names to use localized values for proper multi-language support.
Improved scrolling behavior on cluster detail pages so the specification list no longer blocks access to the cluster configuration.
Fixed a visual display issue with page headers.
Fixed an issue where shipment information was not displayed on the order detail page.
Fixed a mismatch between the quote expiry date shown in the backoffice and the date displayed on the frontend.
Fixed an error that occurred when entering more than 10 digits in the quantity input field.
Fixed an issue where the specifications section was not showing on the product detail page.
Fixed an issue where the enlarged product image overlay was not centered on the page and appeared cut off at the bottom.
Fixed PHP deprecation errors that appeared when running on PHP 8.2 or higher.
Fixed an issue where out of stock products could still be added to the basket despite stock validation being enabled.
Fixed missing payment method costs, carrier and shipping costs on the quote overview page.
Fixed an issue where bulk prices added to products in a cluster were not visible on the frontend cluster detail page.
Fixed a missing gender field in the checkout address form, which could cause incorrect salutations in order confirmation emails.
Fixed an issue where renaming a favorite list was not immediately reflected in the add to favorites button.
Fixed an issue where the default delivery address was displayed twice on the addresses page.
Fixed an issue where changes to the default delivery or invoice address were not immediately visible on the account overview page.
Fixed an issue where delivery addresses from the selected company were not displayed after switching companies.
Fixed broken CSS styling on column headers.
Fixed an issue where cluster products could not be added to a favorites list.