To browse the product catalog, click on “Products” in the main menu. The interface will provide you a filterable list of products in a list view and a catalog view:
You can filter by
On the top right can you switch between list and catalog view and define the number of products shown per page.
You will be provided with information about:
In addition, the different prices are provided for that product. Learn more about the prices in this documentation
To view more details about an order, just click on the order row. The upcoming page will provide the following information:
On top you get informed about:
On the main page you get the following information:
Order Items : Overview on the products that were ordered
With a click on the SKU row, you will also get information about the product name and a product picture. Moreover, you can cancel the order or request a return.
The sku can have the following states:
On the right section you get customer information:
On the page bottom you get information on the order history: A timeline when and why the order status changed
As a platform, 35up connects distributors (vendors) to customers on third-party websites (sellers). The vendors provide the supply of cross-selling products that their customers demand.
Once a sellers' customer purchases a product, 35up will receive the order and pass it on to the vendor. The vendor will then drop-ship the products to the end customer.
We include only large-scale vendors with drop-shipping capabilities on our platform, to ensure a fast and high-quality delivery and product experience for the end customer.
There are two ways to get integrated. For large scale operations or those that require real time order placement, 35up build a proprietary vendor service. For all others, we offer dedicated SFTP servers and a csv integration.
To transfer the shipment status back to 35up, a daily shipments.csv needs to be generated and uploaded to the dedicated SFTP server by the vendor. The file name should include a timestamp and be unique.
The file must contain following columns, including header:
The csv file should provide one line per item_id and order_id
To transfer the return status back to 35up, a daily returns.csv needs to be generated and uploaded to the dedicated SFTP server by the vendor. The file name should include timestamp and be unique.
The file must contain following columns, including header:
The csv file should provide one line per item_id and order_id
As it does for other interfaces, 35up builds a proprietary solution to connect to the vendor’s return services.
To process returns, it is necessary to follow interactions. In case a vendor cannot provide an automated interface, 35up will store respective information in an available space and in a structured way.
If a seller requests a return and the customer ships it back to the vendor, the vendor has to mark the item as arrived, and — if it was returned without reason — assess whether the product is damaged in any way. The confirmation can be done either through the 35up admin interface, or the vendor’s interface (if available), based on:
To ease the process, 35up provides the seller a return label barcode with both sets of information.
If the product never reaches the customer, or if the customer sends the item back without notifying 35up or the seller, the vendor has to create a return and assess any potential product damage.
The confirmation can be done either through the 35up admin interface, or the vendor’s interface (if available), based on:
Returns can be requested within the order detail view. To request a return, click on the “request return” button. A return request is valid when the order is actually delivered already and no other, competing return was requested before. Once the return request was made, the return details can be viewed within the order detail view.
Cancellations can be requested within the order detail view. To request a cancellation, click on the “request cancellation” button. A cancellation request is only valid until the order is getting processed by the vendor.
To include the vendor's products, 35up will provide an SFTP server for the vendor. On this server, the vendor has to upload a product catalog csv file, including all product information availability quantities. The file has to be update regularly, to ensure no out of stock products are listed.
The structure of the csv file is flexible, however, following information need to be provided.
To enable 35up to recommend your products to larger enterprises running SAP, in addition, following information should be provided:
Any additional information is welcomed and used internally.
In the default case, payment collection is the seller’s responsibility. The seller collects the amount from the customer. 35up will invoice the seller, and the vendor will invoice 35up.
The exception to the default case happens when the seller only collects the IBAN from the customer and not the amount itself. In this case, the IBAN can be passed to 35up to collect the cross-selling item’s purchase amount.
The use of a plugin eases the integration and does not require additional coding. We currently have plugins under development for the following shop systems:
Please contact us if you are interested in testing our plugins before release.
To include the vendor's products, 35up will build a proprietary interface to load the vendor’s product feed onto the platform. The product feed can be based on an API, an XML, and EDI, or a CSV/TXT file.
Whatever the format, the feed needs to provide this basic information about each and every product:
To enable 35up to recommend your products to larger enterprises running SAP, in addition, following information should be provided:
Any additional information is welcomed and used internally.
For sellers, 35up provides you with 4 relevant prices:
To change a price of a product, select the product in the product overview and click on the selling price. You can now set a new price and confirm with a click on the check mark.
Because 35up builds a proprietary interface for every vendor, we can work with every format available (API, XML, EDI, csv, txt).
However, the vendor’s order interface has to accept and process the following information, so that orders can be processed:
Moreover, 35up requires a reference order number and a process to receive order status updates from the vendor.
Every product that is ordered at your shop will pass through your backend, the 35up platform, and ultimately end up in the vendor's system.
The customer experiences the journey on your website seamlessly. However, he/she will receive two different packages: one from your shop with the base product and one from the vendor with the cross-selling product.
The return management does not need a separated integration; it can be fully managed through admin. In the admin area, you can request returns for your customers or confirm incoming returned products.
However, a full integration of the return process is beneficial and can fully automate the process. The integration enables you to:
Once the customer requests a return, the request needs to be sent to 35up to create a return label. With this return label, the customer then sends the product directly back to the vendor.
To refund the customer is in your responsibility. 35up will refund your purchase price.
To get an overview of your orders, click on the main menu on “Orders”. The interface will provide you a filterable list of orders:
You can filter by:
You can combine filters and delete them by clicking on the "x" next to the filter value in the bar
Moreover, you can select the number of orders you want to view per page.
The order list provides your the following information:
The order can have the following status:
The basic concept of our API is to keep everything as simple as possible for you and to avoid any unnecessary backend work on your side.
From Developers, For Developers
To achieve this goal, we reduced the required fields in the API recommendation endpoint to a minimum and thereby circumvented the necessity for you to do any backend matching.
A product recommendation consists of two parts. First, you inform us which product is in the basket, and then we automatically provide you with a list of products that would be a great fit. This structured list of products includes all relevant information.
To get your first recommendation, please provide us the following information:
The amount of information is very limited — at this point, no personal information about your customer is necessary.
The basic setup of your account is stored and can be changed in the admin interface. However, you always have the ability to edit the basic information connected to your account during a recommendations request, including:
If you leave this information blank during your request, we will use the default values stored for your account.
To improve the recommendations, you can voluntarily provide more information such as:
None of this information is required, but if you include it, the customer-related information must be GDPR compliant.
Context-related information is a fully flexible tool you can use to give us any information about the shopping situation, including (but not limited to):
The more we know about the product in the basket, the better we can predict which cross-selling item best fits the profile. Therefore, you have the ability to provide us with more information about the product, such as:
In some cases, information about the customer is relevant for the product recommendation and is GDPR compliant. In this case, the following customer information can be provided to make the recommendation fit even better:
To transfer the orders back to the vendor, a daily orders.csv is generated and uploaded to the dedicated SFTP server. The vendor has to download and process the file, and move it to an archives folder. The order file includes the timestamp and is unique.
The file contains following columns, including header:
In case a customer orders more than one different item, multiple lines will be generated within the file.
The recommendations are displayed in the cross-selling widget. Moreover, the widget provides the customer with additional information and allows the customer to add the products to the cart.
The widget is fully configurable and can be adjusted to the look and feel of your website.
You can decide where the widget should be displayed (e.g., in the basket, the checkout, a popup, the product-detail page, or on all pages mentioned). The best-fitting location and amount of integrations depend on your user journey.
The most common location in which to display the cross-selling widget is the basket. The customer has already selected his/her final product and has displayed a willingness to buy, so that is the perfect opportunity to show fitting cross-selling items. Moreover, the basket is a late stage in the journey and shows a high-level of trust in your brand. However, it is still possible to seamlessly add complementary products to the basket without the need to recalculate the final price and taxes.
Another option is to display the widget as a popup between the order detail page and the basket, right after the customer adds the base product. In this case, the customer has already selected his final product and demonstrated a high willingness to buy it. An overlay reminds him about fitting cross-selling items and creates a high awareness of the complementary product. In addition, there is no need to recalculate the final price and taxes.
The cross-selling widget can also be displayed to show complementary items on every product-detail page. This display enriches the product-detail pages. To prevent the complementary item(s) from replacing the base product purchases, an "add to cart" click should not lead to a direct load of the basket, but should stay on the product-detail page.
Some of the product data available in the catalog is very rarely used in the context of rendering cross-selling widgets for the final customers containing recommended products. For this reason, the fields included in the response of a GET /v1/recommendations request is kept to a minimum, making the data modeling simpler and improving performance and response time.
However, some clients, mainly when consuming the data from their backends, may have different data needs because of a number of reasons. For this use-case, the access to extended response fields is made available.
The product data included in the response of
that is not present in the default response of
are considered extended fields. These fields are usually optional and may or may not exist for a certain product.
Some examples are:
In order to receive extended fields in the response of GET /v1/recommendations, the additional query parameter extended=... must be passed together with the usual query parameters. The extended parameter should contain desired optional fields to include in the response’s product model. It can be used in two different ways:
In case the extended parameter is passed, the requested additional fields will be included in the response if they are available. Products without the requested fields will still appear in the provided list of recommendations.
In case the requested extended fields are always required, the parameter extendedRequired=true may be passed, which will cause any product not containing the requested extended fields to not be included in the final response.
Using this parameter is, however, not recommended. Any non-compliant products are filtered in a final post-processing step, which means that it can greatly reduce the number and quality of the recommendations, potentially leading to a much greater number responses with an empty set of recommendations, even if eligible products are still available in the platform.
To ensure a great customer experience, all vendors must ensure delivery within two to three business days via drop-ship to the end customer.
Our sellers provide our customers a 14-day return-without-a-reason policy. The vendor therefore must accept returns and fully refund the seller. However, they can charge a return fee.
You can deactivate every product if you don’t want it to appear in the recommendations on your website. To do so, go to the product overview page and deactivate the item.
To transfer the cancellation status back to 35up, a daily cancellations.csv needs to be generated and uploaded to the dedicated SFTP server by the vendor. The file name should include timestamp and be unique.
The file must contain following columns, including header:
The csv file should provide one line per item_id and order_id
To change the same value for more than one product, select the products by clicking on the checkmark field on the left. To select all products of the page, click on the checkmark field in the table header.
A counter on the left top will provide you with the information on how many products you selected. You can then change the price or the activation status. With a click on “Save changes” you will overwrite the current value by your new settings for all selected products.
To allow a seamless customer journey, the cross-selling items must be added to the basket and processed on the backend. The handover of the data from the cross-selling widget to the basket and the backend depends on the type of integration (API/plugin) and the shop systems involved.
The minimum requirements are as follows:
Based on this information, a new total price can be calculated and a new comprehensive basket can be rendered.
35up offers the possibility of sending requests to predefined endpoints in reaction to events when they happen in the platform. Using this strategy, the cronjobs and other “pull” strategies — often expensive and inevitably delayed in time — can be replaced by “push” strategies that process the necessary data in near real-time and are much less resource-intensive.
For example, in order to detect order status changes, a traditional “pull” strategy would be implemented by regularly querying the GET /v1/orders/:order-id endpoint every X minutes/hours interval. As the number of orders accumulates, this cronjob could quickly result in hundreds of API calls on every run, potentially running for a long time, many times per day. This puts a lot of strain on both client and server systems, even it is clear that several of the references are not expected to be updated anytime soon, sometimes for a few days.
At the same time, the “pull” strategy is not very reliable, as it will always be delayed in time potentially for as much as the interval chosen to run the cronjob. If it runs once per day, the data update will potentially arrive 24h later. Also, when not implemented very carefully, cronjob batch processing can very easily lead to common errors, specially for long-running ones, such as abruptly terminating the process before the queue can reach the end or race conditions leading to deadlocks and duplicated operations.
Using a “push” strategy, both systems can leverage from a fully event-driven approach and handle the requests as they happen, only when they happen, as soon as they happen. The system requirements in this case are very different from the “pull” strategy, requiring the client (the seller) to keep a service permanently running and accepting requests coming from the server (35up). It also requires that the server can react and send predefined requests when events happen.
All the necessary requirements on 35up side are already fulfilled. Our systems are composed by distributed, event-driven microservices, only requiring the externalization of the important events to the client. On the client side, bringing a service up and running, accepting external connections should be an extremely easy task using virtually any programming language and framework existing in the market. In fact, it is estimated that such effort is nearly the same, if not easier, than composing a reliable set of cronjobs to perform the same operations, while also being substantially cheaper.
The webhook system running on the 35up platform is currently in “Beta” version and not publicly available, but sellers wanting to use it may request to join the beta program.
During the testing phase, only a limited number of events are available and there’s no user-friendly interface. In fact, the requests are configured by 35up’s Engineers directly according to the sellers’ specifications and the systems’ current capabilities.
The webhook endpoints on the sellers side are expected to be running 24h per day, every day continuously since the moment the webhook is configured. The webhook service hosted by the client should follow the requirements below:
An important warning: the events system is designed to follow an “at-least-once, entity-ordered” delivery. In an “at-least-once” approach, the client’s system should be able to tolerate duplicated events. Although rare, duplicated requests about the same event can still happen and, therefor, handling of such events must be done in an idempotent manner. By “entity-ordered” it means that the events about the same base entity id are delivered in chronological order, which does not mean that all events are globally chronological.
The constraints above favor a simpler implementation on both the client and server sides, releasing the need of dealing with multiphase commits, acknowledgment protocols and distributed transactions, as long as all events are effectively delivered at least once and idempotency of the operations is always observed.
Events in the 35up platform are named using reverse-domain namespace starting with the io.tfup prefix, followed by the scope/audience “subdomain”, entity (and sub-entities) and event type. New scopes/audiences, entities/sub-entities and event types are expected to be added in the future.
The following events are currently available as triggers for webhook requests containing updates for the different entities used to process a customer order:
In addition to the above, the following inventory event may be interesting to react to product availability changes. Exposing the event below is considered experimental. The number of pushed events can be very (we mean VERY) numerous. Because of that, only the SKUs included in seller-specific product selections (such as custom categories) will be included in outgoing webhook requests:
The documentation is under development and will be published soon. The data in the request body is JSON-encoded.
The payload of each webhook request will include at very least the ****id field of the entity in which the event occurred and its current status.
During the beta program, the payload of webhook requests can be tailored to the sellers’ needs using any of the fields available under the entity in which the event occurred. More details about the available field in the upcoming documentation.
An exception to this rule are the inventory-related events, which can currently only contain the product SKU and the affected fields (price or availability).
Some examples:
Please check the API Reference for all technical details about the API.
To request or patch a return requires authorization and needs to be executed on the backend.
35up uses the standard HTTP authorization, based on base64 encoding.
Most tools and libraries will automatically handle this process.
To place an order, you need to make an authorized POST request to:
All the information needed is part of the request body:
To update a return status, you need to make an authorized PATCH request to:
The ID is the order ID that should be returned.
The request body:
The following terms are used throughout this document:
35up uses the concept of finite state machines (FSM) to control the state transition flow of several parts of the system. In particular, the following entities are currently using a FSM:
In addition to the above, the following entities are planned to use a FSM in the near future:
All state labels are written in English language, lowercase and using underscores (snake_case).
Internally, the 35up platform is designed following a distributed and event-driven architecture adhering to the established good practices in the industry.
When it comes to the state machines in the system, it means that they are independently guided, reacting and sending events that may be coming from other state machines or other sources, internally or externally initiated. With this practice, the size of each state machine is kept to a minimum and only the domain-specific knowledge is needed for each moving part.
An important side-effect of this design is that entity states and state transitions have clearer roles:
Clients implementing a connection to 35up are advised to apply the same principles and design their systems in order to:
Also, keep in mind that despite being Finite State Machines, they are constantly evolving according to the business requirements. The addition of new state labels, although not frequent, is definitely possible. When designing data models to interact with API responses, the clients are advised to be careful if using strict Enum types mirroring the current labels. This data type is well known to break in case unknown values are received.
Currently supported states are the following:
When creating a new order using POST /v1/orders or updating it through a PATCH /v1/orders/:order-id request, the following states are available for the seller:
The following transitions are currently possible by the sellers action by sending a PATCH /v1/orders/:order-id request with {"status": "target_status"} payload:
All the other transitions are triggered by 35up or its commercial partners in reaction to fulfillment actions and unconditional cancellations.
The following states are currently defined for the order items:
When a new order is created using POST /v1/orders, all the required order items are automatically created in created state.
After initial creation, the following states can be used by the seller in PATCH /v1/orders/:order-id/items/:item-id requests:
The following transitions are currently possible by the sellers action on PATCH /v1/orders/:order-id request with {"status": "target_status"} payload:
The state awaiting_return can also be achieved from ordered, shipped and delivered states by the seller action indirectly, creating a new return entity including the desired order item. Please check the return entity below.
All the other transitions are triggered by 35up or its commercial partners in reaction to fulfillment actions and unconditional cancellations.
A return entity currently supports the following states:
The usual return process, with the states described above, starts with the customer making contact with the seller, who requests a new return to 35up. The seller then transmits the shipment information to the customer, who ships the items directly to the vendor. After checking the items, the vendor confirms the return and the seller give the respective refund to the customer.
However, sometimes abnormal return flows also happen. Specially, there are cases where the customer sends the items directly to the seller or to the vendor without formally starting the return process at the 35up platform. In such cases, it is up to the seller or the vendor to create the return in a more advanced state:
A new return can be created by the seller’s action using POST /v1/returns endpoint containing one or more items of the same vendor for a given order. In case items from multiple vendors need to be return, several return entities should be created for the same order.
When creating a return, the following states can be used by the seller as initial states:
The return status can be updated using PATCH /v1/returns/:return-id with a {"status": "target_state"} payload. The following states are available to the seller in such request:
Given the states above, the following transitions are allowed by the state machine and can be triggered by seller’s actions:
All the other transitions are triggered by 35up or its commercial partners in reaction to fulfillment actions and unconditional cancellations.
Some entities have natural child entities, notably the Orders and Returns are connected to one or more Order Items. As such, there may be a mix of different states between order items belonging to the same order or return.
When this happens, the system will advance the state of the parent entity to the most advanced non-conflicting global state that is common to all items in the group and correctly represents the global state of the parent entity.
For example, consider an order containing two order items (item_0 and item_1) of two different vendors (vendor_x and vendor_y, respectively):
The most flexible way to get and show the recommendations is to use our API (see our Reference for all technical details).
You will receive your recommendations with a simple GET request:
The response is a structured JSON, which provides all the information needed to show the recommended cross-selling products.
A comprehensive JSON response for one product is below. However, if more than one product is recommended, the "recommendations" array will provide the additional products in the following structure.
The JSON response always provides the following information, which can be used to display the products.
The vendor object provides the following information for every vendor:
The SKU string shows the unique identifier of a product on our platform.
The price object provides following information:
The name string shows the full name of the product.
Images provides a number of product image URLs with the required "thumbnail."
The descriptions object provides:
The delivery object provides two data points on the delivery time:
Actions are optional actionable URLs that trigger operations on the platform. They are deactivated in the default configuration.
The most flexible way to get the recommendations is to use our API (see our Reference for all technical details).
You will receive your recommendations with a simple GET request:
The API does not require any authentication for the recommendations request.
The most simple recommendations request only requires three additional URL-encoded parameters:
In this example, we use the product "Apple iPhone 12 Pro - Pacific blue" ( URL-encoded - Apple%20iPhone %2012%20Pro%20-%20Pacific%20blue) with the session "test" and the partner ID "35up-test."
No additional parameters are necessary to make your first successful API request.
Every request uses the partner ID to load default settings for:
These values can be adjusted in the admin area. To edit these default values during the request, additional parameters are needed. In this case, we want to take our first example, but change the values to Germany, German, and Euros, with a maximum number of 10 products:
The currency Euro is connected to the country Germany, so in this case no additional parameter is needed.
The more information you provide about the product in the basket, the better the recommendation. To provide additional information, you can add any baseProduct* value, such as:
These additional parameters are not required, but they can provide helpful additional information.
If information about the customer could influence the recommendation, you can submit these values as well. However, you have to take care that all information provided is GDPR compliant. These values can be added as customer.*
Following is an example for an 80-year-old person from Boston:
These additional parameters are not required, but they can provide helpful additional information.
Please check the API Reference for all technical details about the API.
Order placements and updates require authorization and need to be executed on the backend.
35up uses the standard HTTP authorization, based on base64 encoding.
Most tools and libraries will automatically handle this process.
To place an order, you need to make a POST request authorized to:
All the information needed is part of the request body:
The shipping address is crucial. Please make sure you always provide the correct information to avoid unhappy customers and additional costs.
The item’s array provides the information about which product should be ordered in what quantity:
If the order was successfully placed, you will receive a response with the status code 201.
This response contains valuable information:
An order can have one of the following statuses:
To update an order status, you need to make an authorized PATCH request to:
The ID is the order ID received as a response to the POST order request.
The request body:
The response will have the same structure as the POST order request response.
If there is to be a delay between placing an order and dispatching it, this can be solved as follows:
This can be useful if, for example, credit scoring is to be carried out first for the main order.
For every non-technical user, the Admin Area is the main place to get information and change settings. You can reach the Admin Area by clicking on the “Sign In” button on the top menu of 35up.com
The Admin Area is separated into 3 parts. On the left side you can see the main menu, leading to separate pages or sub-menus. On the right top side you can manage your session information and the middle segment provides you with the main information.
Please check the API Reference for all technical details about the API.
The listing of orders requires authorization and needs to be executed on the backend.
35up uses the standard HTTP authorization, based on base64 encoding.
Most tools and libraries will automatically handle this process.
To get a list of all orders, you need to make an authorized GET request to:
The response provides you with a list of orders:
An order can have one of the following statuses:
Based on the ID from the Get Orders request, you can get detailed information about a single order with a GET orders/ID request:
The response body includes all necessary information:
The shipping address is crucial. Please make sure you always provide the correct information to avoid unhappy customers and additional costs.
The items array provides information about which product should be ordered in what quantity:
Please check the API Reference for all technical details about the API.
The request to post payment information requires authorization and needs to be executed on the backend.
35up uses the standard HTTP authorization, based on base64 encoding.
Most tools and libraries will automatically handle this process
To place the SEPA payment information of an order, you need to make an authorized POST request to:
All the information needed is part of the request body.
Please check the API Reference for all technical details about the API.
The API was designed to make the integration for developers as easy as possible. However, the handover of the product data from 35up to your backend requires additional coding on your side when you use the API. The additional 35up product, from this point on, needs to be present in your database.
To achieve the handover using the standard processes that most shop systems use seems to be the easiest, cleanest approach. Of course, the exact execution depends on your system and your system conditions. Here, we identify three best practices:
Many modern shop systems provide the ability to add "configurable" products into the basket. These products do not have to preexist in your product database — they can be created "on the fly." This mechanism is perfect for adding 35up cross-selling to the basket and handing over the data to your backend. In this case, you simply create a new configurable product every time a customer adds a 35up product to the basket and enrich the necessary information based on the product data provided.
This approach ensures the sole use of standard processes and is not likely to require additional changes on the backend.
Another approach to hand over the product data from the front end to the backend is the use of dummy products. If your shop system does not allow for configurable products, dummy products are very often the next best choice. In this case, you create one "35up dummy" product in your product database. Every time a 35up product is added to the basket, you add this dummy product and overwrite the price and name with the values from the product info.
If neither the configurable nor the dummy product approach works for your system, you could add the additional cross-selling items as attributes or comments to an order. In this case, make sure that the information is stored in a structured, repeatable way and that you can use the values to calculate the final price.
To support your effort in handing over the product data, we created an additional API endpoint: "GET products."
This endpoint provides you with all product information based on the SKU. With this endpoint, you only have to hand over the SKU and the quantity, and you can reach all other information on demand later.
The JSON response always provides the following information, which can be used to store the product with all its details in your product database:.
For every vendor, the vendor object provides:
The SKU string shows the unique identifier of a product on our platform.
The price object provides following information:
The name string shows the full name of the product.
Images provides a number of product image URLs, with the required "thumbnail."
The descriptions object provides:
The delivery object provides two data points on the delivery time: