This guide provides an overview of integrating HubBox pickup point functionality into custom-built ecommerce websites, headless commerce setups, or heavily customized platform checkouts using the HubBox Software Development Kit (SDK).
The SDK offers the highest degree of flexibility and control over the user experience and integration logic, allowing you to seamlessly embed HubBox features within your unique checkout flow. This approach requires frontend development to interact with HubBox components and backend development to handle data persistence, filtering logic, and order processing.
configId
provided by your HubBox representative.SDK vs. Tag: If you need a quicker, lower-code frontend solution with less customization required, consider the HubBox Tag Script. The SDK provides maximum control but involves more development effort.
The SDK integration typically involves several key pieces working together:
Launch Experience UI (Retailer Built): You design and build the initial UI elements within your checkout that allow the customer to choose between Home Delivery and Local Pickup (e.g., buttons, toggles, radio options, or integrating pickup as a shipping method choice). Alternatively, you can also implement HubBox's web components for a toggle launch experience.
HubBox Map Widget (SingleWidgetManager
): This core piece, managed by the HubBox @hubbox/single-widget-manager
JavaScript library, loads and displays the interactive map and list of pickup locations within an iframe. It can be presented either embedded directly on the page or within a modal pop-up.
HubBox Web Components (@hubbox/web-components
): Optional but recommended helper components (e.g., <hubbox-modal>
, <hubbox-pickup-confirmation-modern>
) that simplify implementing the modal container and the post-selection confirmation display.
Event-Driven Communication: Your frontend JavaScript interacts with the SingleWidgetManager
using a publish/subscribe event system (events.subscribe
, events.emit
) to trigger searches, open the widget, and receive data about the user's selection.
Configuration: Key settings like your configId
(defining networks, styling), locale
, and environment (sandbox
/production
) are passed during the initialization of the SingleWidgetManager
.
Backend Interaction (Retailer Built): Your frontend JavaScript needs to communicate the selected pickup point data (address, ID, network) to your backend server (e.g., via AJAX/Fetch). Your backend logic must then:
Order Data Sharing (Recommended): Sending details of completed pickup orders back to HubBox via Order Processing API or Tracking Pixel allows you to leverage HubBox data dashboards.
Here's a typical workflow for implementing the SDK:
Install the necessary HubBox packages into your frontend project using NPM or Yarn:
npm install @hubbox/single-widget-manager @hubbox/web-components
# or
yarn add @hubbox/single-widget-manager @hubbox/web-components
Alternatively, if you don't use NPM/Yarn, contact HubBox Integrations for CDN URLs or minified versions.
Remember to import the components if using TypeScript or certain module bundlers:
import { SingleWidgetManager } from '@hubbox/single-widget-manager';
import "@hubbox/web-components"; // Imports and registers custom elements like <hb-widget-modal>
Choose a Pattern: Decide how customers will opt into pickup:
Build the UI: Create the necessary HTML buttons, radio inputs, search fields, etc., within your checkout template. Add appropriate event listeners (e.g., click handlers) to these elements. Alternatively use <hubbox-local-pickup-toggles>
and <hubbox-home-delivery-toggles>
(or <hubbox-local-pickup-multi-button>
and <hubbox-home-delivery-multi-button>
) from the HubBox Web Components.
This approach presents HubBox pickup options directly alongside your standard home delivery rates within the shipping method selection step of your checkout.
User Flow:
Key Considerations:
SingleWidgetManager
(often via iframeParams
during initialization or if supported, via an event emission before opening) so it displays locations for the correct carrier. For example, HubBox can set up multiple configIds
with different networks set up.This user experience pattern presents customers with a clear, high-level choice between "Home Delivery" and "Pickup Points" early in the shipping step of your checkout, often before they interact with the main shipping address form fields.
User Flow:
Key Considerations:
SingleWidgetManager
Instantiate the manager in your frontend JavaScript. Choose one of the following modes:
Modal Mode (Recommended for most checkouts): The widget appears in a pop-up modal.
import { SingleWidgetManager } from '@hubbox/single-widget-manager';
const singleWidgetManager = new SingleWidgetManager({
deferRender: true, // IMPORTANT: Tells manager not to render iframe immediately
iframeUrl: SingleWidgetManager.iframeUrls.PRODUCTION, // Or SingleWidgetManager.iframeUrls.SANDBOX for testing
// healthCheck: false, // Optional: Disable polling check if causing issues
// isDebug: true, // Optional: Enable verbose logging during development
iframeParams: {
configId: "YOUR_HUBBOX_CONFIG_ID", // Provided by HubBox
locale: "en-GB" // Match your store's locale (e.g., en-US, fr-FR)
// Add other config params like 'translations' if needed
}
});
*You'll also need to create/manage the `<hubbox-modal>` component (see Step 4) or use your own modal UI.*
Embedded Mode: The widget appears directly within a container on your page.
import { SingleWidgetManager } from '@hubbox/single-widget-manager';
// Ensure this div exists in your HTML: <div id="hubbox-widget-container"></div>
const widgetContainer = document.getElementById("hubbox-widget-container");
const singleWidgetManager = new SingleWidgetManager({
container: widgetContainer, // REQUIRED: The DOM element to inject iframe into
iframeUrl: SingleWidgetManager.iframeUrls.PRODUCTION, // Or SANDBOX
// isDebug: true,
iframeParams: {
configId: "YOUR_HUBBOX_CONFIG_ID",
locale: "en-GB"
}
});
Connect your Launch Experience UI (from Step 2) to the SingleWidgetManager
.
For Modal Mode:
Add the modal component to your page (can be done on load or dynamically):
<hubbox-modal></hubbox-modal>
// Or create dynamically
// const hubboxModal = document.createElement("hubbox-modal");
// document.body.appendChild(hubboxModal); // Append somewhere appropriate
Configure the modal and link the iframe (usually after manager initializes):
const hubboxModal = document.querySelector("hubbox-modal");
if (hubboxModal && singleWidgetManager.iframeElem) {
hubboxModal.locale = "en-GB"; // Match config locale
hubboxModal.open = false; // Start closed
hubboxModal.appendChild(singleWidgetManager.iframeElem); // Place iframe inside modal
}
Add an event listener to your "Choose Pickup Location" button/link:
const pickupButton = document.getElementById('choose-pickup-button');
const hubboxModal = document.querySelector("hubbox-modal");
const searchInput = document.getElementById('pickup-search-input'); // Optional search input
pickupButton.addEventListener('click', () => {
if (searchInput && searchInput.value) {
// Optional: Pre-fill search if user entered postcode first or if you have user's location
singleWidgetManager.events.emit(singleWidgetManager.topics.emit.MAP_SEARCH_QUERY, searchInput.value);
}
if (hubboxModal) {
hubboxModal.open = true; // Open the modal
}
});
For Embedded Mode:
Ensure the container (#hubbox-widget-container
) is initially hidden (e.g., via CSS class).
Add event listeners to your search input/button:
const searchButton = document.getElementById("hubbox-search-button");
const searchInput = document.getElementById("hubbox-search-input");
const widgetContainer = document.getElementById("hubbox-widget-container");
searchButton.addEventListener("click", function () {
const searchQuery = searchInput.value;
if (searchQuery && widgetContainer) {
singleWidgetManager.events.emit(singleWidgetManager.topics.emit.MAP_SEARCH_QUERY, searchQuery);
widgetContainer.style.display = 'block'; // Show the widget container
// Or remove a hidden class: widgetContainer.classList.remove("hubbox-hidden");
}
});
events.subscribe
)Subscribe to events emitted by the SingleWidgetManager
to react to user actions within the widget iframe.
// --- Essential Callback: Point Confirmed ---
const onCollectPointConfirmed = (messageAndTopic) => {
const collectPointData = messageAndTopic.message; // Contains selected location details
console.log("Pickup Point Confirmed:", collectPointData);
// 1. Populate Frontend Address Fields (Example)
document.querySelector('#shipping-address1').value = collectPointData.address.street1 || '';
document.querySelector('#shipping-company').value = collectPointData.name || ''; // Use 'name' for company
document.querySelector('#shipping-address2').value = collectPointData.address.street2 || ''; // Or use for carrier codes if needed
document.querySelector('#shipping-city').value = collectPointData.address.city || '';
document.querySelector('#shipping-postcode').value = collectPointData.address.postcode || '';
document.querySelector('#shipping-state').value = collectPointData.address.region || ''; // Map 'region' to state/county
// Trigger change events if your checkout requires it for validation/updates
document.querySelector('#shipping-postcode').dispatchEvent(new Event('change', { bubbles: true }));
// 2. Send Data to Your Backend (CRITICAL)
const payload = {
action: 'select_pickup',
collectPointId: collectPointData.id,
collectPointName: collectPointData.name,
collectPointNetwork: collectPointData.network, // e.g., 'ups', 'dpd', 'dhl'
collectPointType: collectPointData.type, // e.g., 'public', 'private'
address: collectPointData.address // Full address object
};
fetch('/your-checkout-api/update-shipping', {
method: 'POST',
headers: { 'Content-Type': 'application/json', /* Add CSRF token etc. */ },
body: JSON.stringify(payload)
})
.then(response => response.json())
.then(data => {
console.log('Backend updated:', data);
// 3. Trigger Shipping Rate Recalculation (if needed by your platform)
// yourPlatform.recalculateShippingRates();
// 4. Show Confirmation UI (See Step 6)
showPickupConfirmation(collectPointData);
// 5. Close Modal (if using modal mode)
const hubboxModal = document.querySelector("hb-widget-modal");
if (hubboxModal) hubboxModal.open = false;
// 6. Potentially move to next checkout step automatically
// yourPlatform.goToNextStep();
})
.catch(error => console.error('Error sending pickup data to backend:', error));
};
// --- Essential Callback: Point De-Selected (User cancels/closes modal) ---
const onCollectPointUnselected = (messageAndTopic) => {
console.log("Pickup Point Unselected");
// 1. Clear Frontend Address Fields (or restore original address)
// ... your logic to clear fields ...
// 2. Send Deselection Event to Backend
fetch('/your-checkout-api/update-shipping', {
method: 'POST',
headers: { 'Content-Type': 'application/json', /* CSRF etc. */ },
body: JSON.stringify({ action: 'deselect_pickup' })
})
.then(() => {
console.log('Backend updated: Pickup deselected');
// 3. Trigger Shipping Rate Recalculation (show home delivery rates)
// yourPlatform.recalculateShippingRates();
// 4. Hide Confirmation UI
hidePickupConfirmation();
})
.catch(error => console.error('Error sending deselection to backend:', error));
};
// --- Other Useful Callbacks ---
const onWidgetReady = (messageAndTopic) => {
console.log("HubBox Widget Iframe Ready:", messageAndTopic.message);
// Widget iframe content is loaded, safe to emit initial search etc.
};
const onBootFailed = (messageAndTopic) => {
console.error("HubBox Widget Boot Failed:", messageAndTopic.message);
// Handle errors - maybe hide pickup option, show error message
};
const onCollectPointSelected = (messageAndTopic) => {
console.log("Point Selected (but not confirmed):", messageAndTopic.message);
// Optional: Use for preliminary UI updates before confirmation
};
// Subscribe to the events
singleWidgetManager.events.subscribe(singleWidgetManager.topics.subscribe.COLLECT_POINT_CONFIRMED, onCollectPointConfirmed);
singleWidgetManager.events.subscribe(singleWidgetManager.topics.subscribe.COLLECT_POINT_UNSELECTED, onCollectPointUnselected);
singleWidgetManager.events.subscribe(singleWidgetManager.topics.subscribe.WIDGET_READY, onWidgetReady);
singleWidgetManager.events.subscribe(singleWidgetManager.topics.subscribe.BOOT_FAILED, onBootFailed);
singleWidgetManager.events.subscribe(singleWidgetManager.topics.subscribe.COLLECT_POINT_SELECTED, onCollectPointSelected);
Key: Implement robust logic within COLLECT_POINT_CONFIRMED
and COLLECT_POINT_UNSELECTED
to update frontend fields, communicate with your backend, and trigger necessary UI changes/recalculations.
For detailed event schemas and more topics, see the Manager.js reference documentation.
After a user confirms a pickup point, display a clear confirmation message.
Option A (HubBox Component): Use the <hubbox-pickup-confirmation-modern>
component.
Add to your HTML (initially hidden):
<hb-core-pickup-confirmation id="hubbox-confirmation" class="hubbox-hidden" locale="en-GB"></hb-core-pickup-confirmation>
In your onCollectPointConfirmed
callback, populate and show it:
function showPickupConfirmation(collectPointData) {
const confirmationElement = document.getElementById('hubbox-confirmation');
if (confirmationElement) {
confirmationElement.address = collectPointData.address;
confirmationElement.openingTimes = collectPointData.openingTimes; // Pass opening times data if available
confirmationElement.name = collectPointData.name;
// Optionally set map coordinates if you want component to show map
// confirmationElement.coords = { lat: collectPointData.address.latitude, lng: collectPointData.address.longitude };
confirmationElement.classList.remove('hubbox-hidden');
confirmationElement.style.display = 'block'; // Ensure it's visible
}
}
function hidePickupConfirmation() {
const confirmationElement = document.getElementById('hubbox-confirmation');
if (confirmationElement) {
confirmationElement.style.display = 'none';
confirmationElement.classList.add('hubbox-hidden');
}
}
(Remember to call hidePickupConfirmation()
in your onCollectPointUnselected
callback).
Option B (Custom UI): Build your own confirmation display using the collectPointData
received in the callback. Ensure you clearly show the selected location name and address.
Your server-side checkout logic needs to:
/your-checkout-api/update-shipping
) that accepts the pickup selection data (ID, name, network, address, pickup flag) sent from the frontend JS.collectPointId
, collectPointNetwork
, collectPointName
) with the order data in your database. This is needed for fulfillment and manifesting.Once the onCollectPointConfirmed
callback fires in the frontend JavaScript, you must send the selected location's details (like collectPointData.id
, collectPointData.network
, collectPointData.name
, and the collectPointData.address
object) to your server-side ecommerce application. This allows your backend to update the user's session/cart, apply correct shipping logic, and save the data when the order is placed. Here are common methods:
How: Add hidden <input>
fields to your main checkout <form>
. In the onCollectPointConfirmed
callback, use JavaScript to set the value
of these hidden fields with the relevant HubBox data (e.g., location ID, network). When the user submits the form (e.g., proceeds to payment), this data is sent along with other form data.
Pros: Relatively simple for traditional multi-page checkouts relying on standard form submissions. Easy to debug by inspecting form data.
Cons: Not suitable for Single Page Application (SPA) checkouts or headless setups where the page doesn't fully reload and data is typically sent via APIs.
Example HTML:
<input type="hidden" name="shipping[hubbox_location_id]" id="hubbox-location-id">
<input type="hidden" name="shipping[hubbox_network]" id="hubbox-network">
<input type="hidden" name="shipping[hubbox_location_name]" id="hubbox-location-name">
Example JS (within onCollectPointConfirmed
/ sendPickupDataToBackend
):
document.getElementById('hubbox-location-id').value = collectPointData.id || '';
document.getElementById('hubbox-network').value = collectPointData.network || '';
document.getElementById('hubbox-location-name').value = collectPointData.name || '';
Deselection (onCollectPointUnselected
): You would typically clear the values of these hidden fields.
How: Create a dedicated API endpoint on your backend server (e.g., /api/checkout/set-hubbox-selection
). In the onCollectPointConfirmed
callback, use JavaScript's Workspace
or XMLHttpRequest
(AJAX) to send a POST request containing the collectPointData
(usually as JSON) to this endpoint. Your backend endpoint receives the data, updates the server-side session/cart object, and typically returns a success/failure response.
Pros: Standard approach for modern web applications (SPAs, headless). Allows for more complex data exchange and immediate feedback/validation from the backend. Doesn't rely on hidden form fields.
Cons: Requires backend API development.
Example JS (within sendPickupDataToBackend
):
function sendPickupDataToBackend(collectPointData) {
const payload = {
action: 'select_pickup',
collectPointId: collectPointData.id,
collectPointName: collectPointData.name,
collectPointNetwork: collectPointData.network,
collectPointType: collectPointData.type,
address: collectPointData.address
};
fetch('/api/checkout/set-hubbox-selection', { // Your backend endpoint
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Include necessary headers like CSRF tokens, Auth tokens etc.
// 'X-CSRF-Token': getCsrfToken()
},
body: JSON.stringify(payload)
})
.then(response => {
if (!response.ok) { throw new Error('Network response was not ok'); }
return response.json();
})
.then(data => {
console.log('Backend successfully updated for pickup:', data);
// Trigger UI updates based on response if needed
// e.g., yourPlatform.recalculateShippingRates();
})
.catch(error => {
console.error('Error sending pickup selection to backend:', error);
// Handle error - maybe revert UI, show message
});
}
Backend Example Concept (Platform-Specific - SFCC Example Provided for Illustration): The backend endpoint needs to receive the JSON payload, validate it, update the cart/shipment object, and potentially recalculate shipping. The following is a conceptual example using SFCC server-side JS:
// Example SFCC Controller Action (Conceptual - from original doc)
"use strict";
var server = require("server");
server.extend(module.superModule);
var csrfProtection = require("*/cartridge/scripts/middleware/csrf");
var HubBoxHelper = require("*/cartridge/scripts/helpers/HubBoxHelper"); // Assumes helper exists
var BasketMgr = require("dw/order/BasketMgr");
// ... other requires ...
// Endpoint logic (simplified)
server.post("SetHubboxSelection", server.middleware.https, csrfProtection.validateAjaxRequest, function (req, res, next) {
var currentBasket = BasketMgr.getCurrentBasket();
if (!currentBasket) { /* Handle error */ }
var shipment = currentBasket.defaultShipment;
// Example using data passed in request body (req.body) or form (req.form)
var requestData = JSON.parse(req.body); // If using fetch with JSON body
if (requestData.action === 'select_pickup' && requestData.collectPointId /* Add validation */) {
// Use helper or logic to update the SFCC Shipment object
HubBoxHelper.copyHubBoxDataToShipment(
shipment,
requestData.collectPointId,
requestData.collectPointName,
requestData.collectPointType,
requestData.collectPointNetwork
// Pass address object too if needed by helper
);
// Update session/recalculate shipping etc.
res.json({ success: true, message: "HubBox selection saved." });
} else if (requestData.action === 'deselect_pickup') {
HubBoxHelper.clearHubBoxDataFromShipment(shipment);
// Update session/recalculate shipping etc.
res.json({ success: true, message: "HubBox selection cleared." });
} else {
res.setStatusCode(400);
res.json({ error: true, message: "Invalid request." });
}
return next();
});
module.exports = server.exports();
Deselection (onCollectPointUnselected
): Make a similar Workspace
call to your endpoint, perhaps with action: 'deselect_pickup'
, so the backend can clear the relevant data and revert to home delivery logic.
How: In the onCollectPointConfirmed
callback, use document.cookie
to set a cookie containing the necessary HubBox data (e.g., location ID). Your backend code then needs to read this cookie value during checkout processing to identify the pickup selection.
Pros: Can sometimes work as a last resort on highly restrictive platforms where modifying forms or making JS API calls is impossible.
Cons: Less reliable (cookies can be blocked/cleared), limited data size, potential security concerns if not handled carefully (use HttpOnly, Secure flags where appropriate on server-set cookies, be mindful of data in client-set cookies). Generally not recommended unless other methods fail.
Deselection (onCollectPointUnselected
): You would need to delete the cookie or set its value to indicate deselection.
Choose the method that best suits your checkout architecture (Traditional Form vs. SPA/Headless) and technical constraints. Using a Custom REST API is generally the most robust and recommended approach for modern web applications.
Depending on your chosen carrier for pickup services, you may need to make some changes to the way in which your WMS creates shipments and/or generates labels.
In some instances, you will need to take the ID
property from the COLLECT_POINT_CONFIRMED
event and either place this code within the shipping address for the order or store as metadata.
Please refer to our documentation on Manifesting Pickup Order for full details or contact HubBox if you require support.
Sharing details of completed pickup orders back to HubBox enables powerful data analysis and reporting features within the HubBox Console dashboard. This provides insights into pickup adoption rates, ROI, carrier performance, and operational trends. We strongly encourage implementing one of these methods.
Order Processing API (Server-to-Server):
collectPointId
), Network (collectPointNetwork
), order value, currency, item count, and potentially anonymized customer details.Tracking Pixel (Frontend):
collectPointId
)collectPointNetwork
)Choose the method that best fits your technical capabilities and reliability requirements. The Order Processing API is generally preferred for accuracy. Data is anonymised and does not include any personally identifiable information (PII) or payment/card information (PCI).
Launch / Confirmation Components: If using HubBox Components for the launch experience, style them using CSS ::part()
and CSS Custom Properties (:root
variables) as detailed in the HubBox Components Documentation.
Widget Modal/iframe: The internal look and feel of the map widget itself (colors, pins, map style within the iframe) is primarily controlled by the configId
you use during SingleWidgetManager
initialization. This configuration is managed within the HubBox platform. See the Widget Configuration Documentation.
Set Locale: Pass the correct locale code (e.g., "fr-FR"
) in the iframeParams.locale
when initializing SingleWidgetManager
.
Override Text: Pass a translations
object within iframeParams
to override default text within the widget iframe. For text in Components rendered directly on your page (like the confirmation component), set the .translations
property on the component instance after creating it. Refer to HubBox Components Documentation for keys.
Implementing features like eligibility and rate filtering with the SDK requires custom logic within your application, triggered by SDK events.
Concept: Prevent users from selecting pickup if their cart contains ineligible items.
Implementation: 1. Develop backend logic to determine if a cart is eligible based on its contents (checking product attributes, categories, dimensions etc. against your rules). Expose this eligibility status (e.g., via an API endpoint or data attribute on the page). 2. In your frontend JavaScript, before triggering the HubBox widget (e.g., before opening the modal or running a search), check the cart's eligibility status (fetch from backend or read from page data). 3. If ineligible, disable or hide your "Choose Pickup Location" button/UI elements. Optionally display a message explaining why pickup is unavailable.
Below is an example snippet for running eligibility logic to show or hide the pickup UI.
function isBasketEligibleForPickup(cartItems) {
return cartItems.every((item) => item.isEligibleForPickup); // Assume 'isEligibleForPickup' exists on items
}
// ... In your checkout area ...
const cartItems = getCartItems(); // Replace with your platform's method
const pickupOptionElement = document.getElementById("pickupOption");
if (isBasketEligibleForPickup(cartItems)) {
pickupOptionElement.style.display = "block"; // Show pickup option
} else {
pickupOptionElement.style.display = "none"; // Hide pickup option
}
Concept: Show only relevant shipping rates after the user selects either Home Delivery or Local Pickup. Essential for "Toggles" style launch experiences.
Implementation:
1. In your frontend JavaScript onCollectPointConfirmed
callback, send the "pickup selected" flag and potentially the chosen network/location details to your backend API. In onCollectPointUnselected
, send a "home delivery selected" flag.
2. Your backend logic (which calculates/retrieves shipping rates) must receive this flag.
3. Based on the flag, filter the list of available shipping methods before returning them to the frontend for display. (e.g., If pickup selected, only return rates configured for pickup; if home selected, only return home rates).
4. Your frontend may need to trigger a refresh of the shipping rates section after the selection is made and confirmed by the backend.
More Info: Shipping Rate Filtering Documentation.
Below is an example snippet for filtering shipping rates based on whether the order is a pickup order.
function filterShippingRates(availableRates, isPickupOrder) {
if (isPickupOrder) {
return availableRates.filter((rate) => rate.id === "local_pickup"); // Only keep "Local Pickup"
} else {
return availableRates.filter((rate) => rate.id !== "local_pickup"); // Filter out "Local Pickup"
}
}
// ... Somewhere in your checkout process ...
const isPickupOrder = order.hasFlag("pickup"); // Replace with your logic
const allShippingRates = getAvailableShippingRates(); // Replace with your platform's method
const filteredRates = filterShippingRates(allShippingRates, isPickupOrder);
// Update the UI or recalculate totals using 'filteredRates'
Thorough end-to-end testing on a staging/development environment is critical for SDK implementations.
SingleWidgetManager
using the SingleWidgetManager.iframeUrls.SANDBOX
URL and ensure your backend is configured to use HubBox Sandbox API endpoints (if applicable).WIDGET_READY
events and no BOOT_FAILED
errors.console.log
heavily within your callbacks (onCollectPointConfirmed
, onCollectPointUnselected
) to verify they fire correctly and receive the expected data (messageAndTopic.message
).To access HubBox's UAT guide, visit the Testing Documentation.
An example project demonstrating basic SDK setup and interaction is available.
Download SDK Integration Example (Ensure you are logged in to download. Contact your HubBox representative if you need access.)
The SDK offers great flexibility but requires careful implementation. If you encounter issues or have questions about specific SDK methods, events, component usage, or integration strategies, please contact the HubBox Client Support or Integrations team at clientsupport@hubbox.com. Provide relevant code snippets, configuration details, and steps to reproduce the issue.