HOMEDASHBOARDSINGRAM / TD SYNNEX CONSOLE — KB
kb-dashboard-ingram-tdsynnex-console.html
KNOWLEDGE BASE // DASHBOARDS // VENDOR PROCUREMENT
MSP ProDesk — Ingram & TD SYNNEX Console
Complete reference for the MSP ProDesk Vendor API Console — a six-pane procurement dashboard covering product search, order building, project/BOM optimization, order tracking, analytics, and intelligence across Ingram Micro and TD SYNNEX. Includes the full API audit, documented vendor limitations, wiring architecture, and proxy activation checklist.
INGRAM MICRO v6 API TD SYNNEX SOAP + ION PROXY WIRED 6 PANES API AUDIT COMPLETE
FILEdashboard-ingram-tdsynnex-console.html
INGRAM API BASEapi.ingrammicro.com:443/resellers/v6
SYNNEX HARDWAREws.synnex.com (SOAP/XML)
SYNNEX CLOUDion.tdsynnex.com/api/v3 (REST)
PROXY PATTERNPROXY_BASE + apiFetch() + DEMO_MODE
01
WHAT THIS TOOL DOES

The MSP ProDesk Vendor API Console is a single-file HTML dashboard that centralizes MSP procurement workflows across Ingram Micro and TD SYNNEX. It gives procurement teams a unified interface for product search, real-time pricing, BOM optimization, order submission, shipment tracking, spend analytics, and market intelligence — without switching between vendor portals.

The console runs in full demo mode out of the box — all data is generated from the static products[] array and hardcoded mock objects. When PROXY_BASE is set, each action calls the proxy which routes to the real vendor APIs and returns normalized JSON. The UI, render functions, and interaction model are production-ready and require no changes to go live.

INTEGRATION STATUS Ingram Micro: real v6 REST API exists and is fully documented at developer.ingrammicro.com. TD SYNNEX hardware: real SOAP/XML API only — no REST equivalent. TD SYNNEX cloud/licenses: StreamOne Ion REST API. All paths in the dashboard have been corrected to match the real APIs. See Section 02 for the full audit.
SIX PANES
Search
Real-time product search across 24 pre-loaded SKUs. Filter by vendor, category, stock status. Instant as-you-type filtering. Activates live when proxy serves /api/ingram/catalog.
Order Builder
Configure and submit purchase orders. Cart with quantity editing, vendor selection, shipping method, PO number, line items. Submit wires to POST /resellers/v6/orders (Ingram) or SYNNEX SOAP when proxy is live.
Project / Quote Builder
Build a Bill of Materials, compare Ingram vs SYNNEX pricing per line, run automated procurement optimization (cheapest + in-stock routing), apply client hardware profiles, export BOM as CSV.
Tracking
Enter an order ID or PO number to fetch shipment status. Three hardcoded demo orders (Ingram + SYNNEX) with full step-by-step timelines. Sidebar order tiles click through to this pane. Activates live via GET /resellers/v6.1/orders/{orderNumber}.
Analytics
MTD spend, order count, avg lead time, fill rate, vendor split bars, top products table, and a rolling API call log. All static in demo mode. Activates live when proxy serves aggregated spend data.
Intelligence
Stock shortage predictions, hardware substitution recommendations, price trend tracking, client hardware profiles, and auto-order submission cards. All static demo data — no vendor API supports these features natively. Requires proxy-side analytics layer.
02
API AUDIT FINDINGS

Every data-fetching action, button, and displayed endpoint path was audited against live vendor documentation before any code was changed. The following table is the complete finding set.

ORIGINAL PATH IN CODEVERDICTCORRECTED TO
/v1/inventory/search Wrong version GET /resellers/v6/catalog/productsearch — Ingram v6 REST
SOAP pnaserviceV05 — TD SYNNEX hardware
GET /v1/pricing/reseller Wrong version + method POST /resellers/v6/catalog/priceandavailability — Ingram (POST body, up to 50 SKUs)
SOAP pnaserviceV05 — TD SYNNEX
GET /v1/inventory/{sku}/detail Wrong version GET /resellers/v6/catalog/details/{ingramPartNumber} — Ingram v6
POST /v1/orders/create Wrong version + path POST /resellers/v6/orders — Ingram v6
SOAP poserviceV02 — TD SYNNEX hardware
POST /v1/orders/validate Endpoint does not exist No separate validation endpoint in Ingram API. Validation is server-side during POST /orders itself. Kept as UI-only preflight with code comment.
GET /v1/orders/{id}/tracking Wrong version GET /resellers/v6.1/orders/{orderNumber} — returns status + shipment data
SOAP posserviceV02 — TD SYNNEX PO status
POST /v1/returns/rma Wrong version + path POST /resellers/v6/returns/createrequest — Ingram v6
GET /v1/cloud/subscriptions Wrong path GET /resellers/v6/catalog/productsearch?keyword=cloud for Ingram catalog; cloud subscription management is a separate XI API
PUT /v1/licenses/fulfill Wrong API + method POST /ion.tdsynnex.com/api/v3/accounts/{id}/subscriptions — StreamOne Ion REST (cloud/SaaS only)
GET /v1/procurement/optimize Does not exist No vendor API. Optimization is local logic in the proxy comparing P&A responses from both vendors. Documented in code comment.
GET /v1/clients/{id}/hardware-profile Does not exist No vendor API. Client profiles are local configuration data in the proxy or a CRM. Documented in code comment.
GET /v1/projects/export?format=csv Does not exist No vendor API. BOM export is client-side CSV generation. Documented in code comment.
CRITICAL — TD SYNNEX HAS NO REST API FOR HARDWARE This is the most important finding. TD SYNNEX hardware product search, pricing (P&A), and order submission use a legacy SOAP/XML API only (ws.synnex.com/webservice/). There is no REST JSON equivalent. Your proxy must call the SOAP endpoints and normalize the XML responses to JSON before returning them to this console. The modern StreamOne Ion REST API (ion.tdsynnex.com/api/v3) covers cloud subscriptions and licenses only — not physical hardware.
03
ARCHITECTURE & WIRING

The console is a self-contained single HTML file. No framework, no CDN dependencies except Google Fonts. All rendering is pure DOM manipulation using template literals.

WIRING PATTERN
// Three variables control the entire integration const PROXY_BASE = ''; // '' = same origin | 'https://func.azurewebsites.net' = cross-origin const DEMO_MODE = true; // true = silent fallback | false = log errors to console // apiFetch — attempts real fetch, falls back to fn() if PROXY_BASE unset or request fails async function apiFetch(url, fallbackFn, options = {}) { if (!PROXY_BASE) return fallbackFn(); // instant fallback in demo mode try { const r = await fetch(PROXY_BASE + url, { ...options, signal: AbortSignal.timeout(8000), headers: { 'Accept': 'application/json', ... } }); if (!r.ok) throw new Error('HTTP ' + r.status); return await r.json(); // ← live data } catch(e) { if (!DEMO_MODE) console.warn('[ProDesk]', url, e.message); return fallbackFn(); // ← fallback to mock } } // loadDashboard — called on init; activates when PROXY_BASE is set async function loadDashboard() { // return apiFetch('/api/ingram/catalog?limit=24', () => products); renderProducts(products); // demo path } // Auto-refresh every 30 seconds startAutoRefresh(30000);
PROXY ARCHITECTURE (TWO VENDORS, ONE INTERFACE)

The proxy must present a single normalized JSON API to this console regardless of which vendor backs each request. Ingram calls are pure REST. SYNNEX hardware calls require SOAP translation. The console never knows which protocol is used underneath.

// Proxy must expose these normalized REST endpoints: GET /api/ingram/catalog → calls Ingram GET /resellers/v6/catalog/productsearch POST /api/ingram/pricing → calls Ingram POST /resellers/v6/catalog/priceandavailability POST /api/ingram/orders → calls Ingram POST /resellers/v6/orders GET /api/ingram/orders/{id} → calls Ingram GET /resellers/v6.1/orders/{orderNumber} GET /api/synnex/catalog → calls SYNNEX SOAP pnaserviceV05 · normalizes XML → JSON POST /api/synnex/pricing → calls SYNNEX SOAP pnaserviceV05 · normalizes XML → JSON POST /api/synnex/orders → calls SYNNEX SOAP poserviceV02 · normalizes XML → JSON GET /api/synnex/orders/{id} → calls SYNNEX SOAP posserviceV02 · normalizes XML → JSON POST /api/synnex/licenses → calls StreamOne Ion POST /ion.tdsynnex.com/api/v3/accounts/{id}/subscriptions // Proxy-side only (no vendor API): GET /api/bom/optimize → proxy calls both P&A endpoints, runs cost+stock comparison GET /api/clients/{id}/profile → proxy reads from local config or CRM
04
PRODUCT SEARCH PANE

The default landing pane. Shows a search bar, category/stock filter chips, and a responsive product grid. Filtering is instant as-you-type — no button press needed after initial load. The search button fires runSearch() and also shows the response terminal panel below the grid.

Search input
Matches against name, sku, cat, and desc fields. Case-insensitive. Live filtering via input event listener. Demo: filters the 24-item static products[] array.
Vendor filter
All / Ingram / SYNNEX — filters by p.vendor field. Live. Activates live: proxy sends vendor parameter to respective API.
Category chips
Networking, Servers, Storage, Software, Cloud, Endpoints. Toggle on/off. Multiple active = OR logic. Live filter against p.cat.
Stock chips
In Stock (>5), Low Stock (1–5), Backorder (0). Toggle. Live filter against p.stock.
Sort dropdown
Best Match, Price Low→High, Price High→Low, Stock Highest, Lead Time. Sorts the current filtered result set. sortProducts() function, no API call.
Product card
Click anywhere → viewProduct(id) opens modal with full detail, pricing, and MSRP estimate. "Add to Order" button → addToCart(id). "Check Pricing" → logs POST /resellers/v6/catalog/priceandavailability.
Response terminal
Appears below grid when Search is clicked. Shows simulated JSON response including endpoint, vendor, result count, cache status, and response time. Color-coded syntax highlighting.
ACTIVATES WHEN PROXY IS LIVE Replace renderProducts(products) in loadDashboard() with apiFetch('/api/ingram/catalog?limit=24', () => products).then(renderProducts). The proxy calls GET /resellers/v6/catalog/productsearch?keyword=all&pageSize=50&pageNumber=1 and returns the same product object shape.
05
ORDER BUILDER PANE

Two-column layout: left has order configuration (vendor, PO number, shipping, line items); right shows the cart summary, order total, estimated lead time, and an API endpoint preview panel.

Vendor selector
Select Ingram or SYNNEX for this order. Sets which API endpoint the submit action targets. UI only in demo mode.
Customer PO Number
Free-text input. Populates customerOrderNumber in the Ingram POST /resellers/v6/orders request body, or the equivalent SYNNEX SOAP field.
Line items grid
Pre-populated from cart. SKU, description, unit price, quantity, line total. Remove button calls removeItem(i). "Add Line Item" appends a blank row. renderLineItems() syncs with cartData[].
Validate Order button
UI-only preflight. Fires a toast "Order Valid". Ingram has no separate validation endpoint — validation is part of the POST /orders call itself. The button is a UX convenience only.
Submit via API button
Calls logApiCall('POST', '/resellers/v6/orders', 'ingram') and shows the response terminal with a 201 Created mock response. Activates live when proxy is set.
Cart summary (right)
Quantity +/- controls. Subtotal, $145 flat shipping (when cart >0), 0% reseller tax. Grand total. Synced with the order builder line items grid.
06
PROJECT / QUOTE BUILDER PANE

BOM-driven procurement optimization. Three hardcoded demo items (Fortinet FG-70F, Ubiquiti USW-48-POE, Ubiquiti U6 Pro). Compares Ingram vs SYNNEX pricing and stock for each line, then routes each item to the cheapest available distributor.

Client Profile selector
Acme Corp, Globex Inc, Initech. Applies a client's preferred brands and distributor. Calls applyClientProfile() → logs /[LOCAL] client-profiles/{id}. No vendor API — local config data only.
Cache pill
Shows CACHE HIT or CACHE MISS. Simulates a Redis TTL cache (Price: 5min, Inventory: 3min). The cacheGet() / cacheSet() functions use an in-memory JS object — not a real Redis instance. Proxy should implement real Redis caching.
BOM table
Columns: Product, Hardware Fingerprint, Qty, Ingram Price (+ stock), SYNNEX Price (+ stock), Optimal. Fingerprint format: mfr|partNumber (e.g. fortinet/fg70f) for vendor-agnostic SKU matching.
⚡ Optimize Procurement
Runs runProcurementOpt(). For each BOM line: if Ingram has stock AND Ingram total ≤ SYNNEX total → route Ingram, else SYNNEX. Shows optimal plan with per-item routing and grand total. Logs POST /resellers/v6/catalog/priceandavailability [BOM-OPT]. No vendor API for optimization — all logic is local.
↓ Export BOM
Logs /[LOCAL] bom-export.csv [client-side]. No vendor API. CSV generation is client-side only. To implement: build a CSV string from bomData[] and trigger a download via Blob URL.
Submit All Orders button
Fires two logApiCall entries — POST /resellers/v6/orders (Ingram) and POST /ws.synnex.com/webservice/poserviceV02 [SOAP] (SYNNEX). Activates live when proxy is set.
07
ORDER TRACKING PANE

Enter any order ID in the input and click TRACK. Three demo orders are wired: IM-2024-091422 (Ingram, in transit), TD-2024-003291 (SYNNEX, processing), IM-2024-091380 (Ingram, delivered). Unknown IDs fall back to the first demo order.

Timeline steps
Each step has a state: done (green check), active (blue pulsing ring), pending (empty circle). A vertical connector line links steps. Rendered by fetchTracking() from the allTrackingOrders object.
Shipment details panel
Carrier, tracking number, estimated delivery, item count, weight. Updates when tracking is fetched. fetchTracking() loops over .stat-row elements and matches by label text.
Pending orders panel
Three static orders from pendingOrdersData[]. Shows order ID (color-coded by vendor) and status (color-coded by state). No live data in demo mode.
Sidebar order tiles
Five status tiles in the sidebar (In Transit, Processing, Delivered, Backordered, Shipped). Clicking any tile calls jumpToTracking(orderId) which switches to the tracking pane and runs a fetch.
ACTIVATES WHEN PROXY IS LIVE In fetchTracking(), replace const order = allTrackingOrders[id] with const order = await apiFetch('/api/ingram/orders/' + id, () => allTrackingOrders[id] || allTrackingOrders['IM-2024-091422']). The proxy calls GET /resellers/v6.1/orders/{orderNumber} (Ingram) or the SYNNEX SOAP PO Status service and returns a normalized step array.
08
ANALYTICS PANE

Opens via the Analytics nav pill. Triggers animateAnalytics() which animates sparkline bars and vendor split bars. All values are hardcoded — no live data connection in any field of this pane.

MTD Spend ($84.2K)
Hardcoded. Activates live when proxy aggregates invoice data from GET /resellers/v6/invoices/search.
Orders Placed (47)
Hardcoded. Activates live from GET /resellers/v6/orders/search?customerNumber={id} response total.
Avg Lead Time (2.1d)
Hardcoded. No direct vendor API for lead time analytics — would need to compute from order created vs shipped timestamps.
Fill Rate (96.8%)
Hardcoded. Would require computing (orders fulfilled on time) / (total orders) from historical order data.
Vendor Split (Ingram 68% / SYNNEX 32%)
Hardcoded bars. Activates live when proxy aggregates spend by vendor from invoice history.
Top Products This Month
Four hardcoded product tiles. Activates live from order history aggregation.
API Call History log
Live — updated on every action in the console. Shows method, path, vendor, and timestamp for the last 12 calls. Driven by logApiCall() which prepends to apiLogEntries[].
09
INTELLIGENCE PANE

Five intelligence cards. All static demo data. No vendor API natively supports stock predictions, hardware substitution rankings, or client hardware profiles — these features require a proxy-side analytics layer built on top of vendor API data.

📉 Stock Shortage Prediction
Four items with backorder risk assessment. Demo data only. Live: proxy polls POST /resellers/v6/catalog/priceandavailability periodically, tracks stock trends, flags items trending toward 0. No vendor pushes this data.
🔄 Hardware Substitution Engine
Ranked alternatives for out-of-stock items with compatibility score and in-stock status. Demo data only. No vendor API for substitution. Requires a local compatibility matrix maintained in the proxy.
📈 Price Trend Tracking
30-day price range bars with current position and percent change. Demo data only. Live: proxy stores historical P&A responses in a database and computes trends over time.
🏢 Client Hardware Profiles
Preferred vendors, brands, and deployment count per client. Demo data only. No vendor API. Maintained as local config in the proxy or pulled from a CRM.
🤖 Auto Order Submission cards
Three status cards showing post-BOM-approval auto-submitted orders. Demo data only. Live: proxy stores submitted order IDs and polling results from GET /resellers/v6.1/orders/{id}.
11
INGRAM MICRO v6 API REFERENCE
CONFIRMED REAL — developer.ingrammicro.com Production base: https://api.ingrammicro.com:443/resellers/v6
Sandbox: https://api.ingrammicro.com:443/sandbox/resellers/v6
Register: developer.ingrammicro.com — requires active Ingram Micro reseller account number.
AUTHENTICATION
// Step 1: Get OAuth2 bearer token POST https://api.ingrammicro.com:443/oauth/oauth20/token Body: grant_type=client_credentials&client_id={id}&client_secret={secret} Returns: { access_token, expires_in } // Step 2: Include in all API requests Authorization: Bearer {access_token} IM-CustomerNumber: {your-customer-number} // required on all calls IM-CountryCode: US // required on all calls IM-CorrelationID: {uuid} // unique per request for tracing
ENDPOINTMETHODWHAT IT RETURNSUSED BY
/resellers/v6/catalog/productsearchGETProduct list by keyword, vendor part number, UPC, or categorySearch pane
/resellers/v6/catalog/details/{imPartNumber}GETFull product detail — specs, description, images, content dataProduct modal
/resellers/v6/catalog/priceandavailabilityPOSTReseller pricing, discounts, stock by warehouse, lead time (up to 50 SKUs per call)Check Pricing, BOM Optimizer
/resellers/v6/ordersPOSTCreate and place a new orderOrder Builder, Project Builder
/resellers/v6.1/orders/{orderNumber}GETFull order detail including status, line items, shipment info, trackingTracking pane
/resellers/v6/orders/searchGETSearch orders by PO number, order number, status, date rangeAnalytics (order count)
/resellers/v6/returns/createrequestPOSTCreate RMA/return requestsidebar endpoint list
/resellers/v6/invoices/searchGETSearch invoices by PO, order number, date rangeAnalytics (MTD spend)
12
TD SYNNEX API REFERENCE
TWO COMPLETELY SEPARATE APIS — READ CAREFULLY TD SYNNEX operates two distinct APIs for different product types. Hardware (switches, servers, firewalls) uses a legacy SOAP/XML web service. Cloud/SaaS (Microsoft 365, Acronis, etc.) uses the StreamOne Ion REST API. You need to understand which is which before building the proxy.
HARDWARE — SOAP/XML (ws.synnex.com)

These are the endpoints that back the hardware product search, pricing, and order workflows in this console. Your proxy must call SOAP, parse the XML response, and return JSON.

// Request access: helpdeskus@tdsynnex.com — subject "Register for Price & Availability (PA) API access" // Requires: EC Express account + customer number POST https://ws.synnex.com/webservice/pnaserviceV05?wsdl // P&A query (SOAP) POST https://ws.synnex.com/webservice/poserviceV02?wsdl // Place Purchase Order (SOAP) POST https://ws.synnex.com/webservice/posserviceV02?wsdl // PO Status query (SOAP) // Proxy: Parse XML response, normalize to JSON matching this console's data shapes // See Section 03 for normalized proxy endpoint paths
CLOUD / LICENSES — StreamOne Ion REST (ion.tdsynnex.com)

Used only for cloud subscription management — Microsoft 365, Acronis, and similar SaaS products. This is what backs the /v1/licenses/fulfill call that was corrected during the audit.

// Auth: OAuth2 Bearer token from StreamOne Ion portal // Docs: https://docs.streamone.cloud/ GET https://ion.tdsynnex.com/api/v3/accounts/{id}/customers GET https://ion.tdsynnex.com/api/v3/accounts/{id}/subscriptions POST https://ion.tdsynnex.com/api/v3/accounts/{id}/subscriptions // create subscription GET https://ion.tdsynnex.com/api/v3/accounts/{id}/invoices
13
DOCUMENTED API LIMITATIONS

These are genuine vendor API gaps — features the console displays that no vendor API can fulfill directly. Each is documented in the dashboard source code with a comment. Your proxy must implement workarounds where noted.

FEATURELIMITATIONPROXY WORKAROUND
Ingram order validation No /orders/validate endpoint exists. Validation happens server-side during POST /orders. The Validate button is a UI-only preflight. Wire it to call POST /orders with simulate=true query param if Ingram ever adds dry-run support; otherwise leave as toast-only.
TD SYNNEX REST for hardware No REST JSON API for hardware P&A or order submission. SOAP/XML only. Proxy must call SOAP endpoints and normalize XML responses to JSON. The console never knows SOAP was involved.
Procurement optimization endpoint No vendor API for routing optimization. Proxy calls both P&A APIs for each BOM SKU, compares total price + availability, returns the optimal vendor assignment. All logic is in the proxy.
Client hardware profiles No vendor API stores MSP client preferences. Maintain a local JSON config file in the proxy (or pull from CRM) keyed by client ID. Return on GET /api/clients/{id}/profile.
Stock shortage prediction No vendor API pushes stock trend data. Proxy stores historical P&A snapshots in a database and computes trends. Requires periodic polling of P&A for watched SKUs.
Hardware substitution engine No vendor API for compatibility scoring. Maintain a local compatibility matrix. When a SKU is out of stock, proxy looks up alternatives in the matrix and checks their P&A status.
Price trend history Ingram and SYNNEX APIs return current price only — no history endpoint. Proxy stores P&A responses in a time-series database (Cosmos DB, InfluxDB) and serves historical data from its own store.
BOM CSV export No vendor API. Export is client-side only. Generate CSV from bomData[] using Blob + URL.createObjectURL(). No proxy call needed.
CONFIGURATION GUIDE
C1
PREREQUISITES
Ingram Micro developer account
Register at developer.ingrammicro.com. Requires an active Ingram reseller account number in good standing. Sign-up authorizes your account for API channel access. You receive client_id and client_secret for OAuth2. Approval takes ~2 business days. Sandbox is available immediately.
TD SYNNEX EC Express account
You must have an EC Express account to access the SOAP P&A and order APIs. Email helpdeskus@tdsynnex.com with subject "Register for Price & Availability (PA) API access". You receive a customer number and EC Express credentials.
TD SYNNEX StreamOne Ion credentials
For cloud/license management only. Log in to StreamOne Ion portal, navigate to Settings → Users, edit the user, click "Request New Credentials" under OAuth Credentials. Copy the Refresh Token and use it to generate Bearer tokens.
Azure Function Proxy
All vendor credentials must live in Azure Key Vault — never in browser code. The Function App reads them via Managed Identity. It translates the console's normalized REST calls to vendor-specific API calls (REST for Ingram, SOAP for SYNNEX hardware).
C2
PROXY CONFIG VARIABLES
// Near top of the <script> block — two variables control everything const PROXY_BASE = ''; // '' = same origin as HTML file (SharePoint, same Azure site) // 'https://your-funcapp.azurewebsites.net' = cross-origin proxy const DEMO_MODE = true; // true = all fallback errors are silent (default, safe) // false = fallback errors logged to browser console (use during testing)
TO ACTIVATE LIVE DATA Set PROXY_BASE to your Function App URL, set DEMO_MODE = false while testing, then replace the static data calls in loadDashboard() and individual action functions with apiFetch() calls. The console renders correctly with either real or demo data — the render functions are data-source agnostic.
C3
WIRE INGRAM MICRO
  • 1
    Get OAuth tokenYour proxy must call POST https://api.ingrammicro.com:443/oauth/oauth20/token with grant_type=client_credentials and your client credentials from Key Vault. Cache the token (expires in ~3600s). Add it as Authorization: Bearer {token} to every downstream call.
  • 2
    Product search endpointWire GET /api/ingram/catalog?keyword={q}&pageSize=50 → proxy calls GET /resellers/v6/catalog/productsearch?keyword={q}&pageSize=50&pageNumber=1. Map the response catalog[] array to the console's products[] shape (vendor, sku, name, desc, price, stock, lead, cat).
  • 3
    Price and availabilityWire POST /api/ingram/pricing (body: {skus:[]}) → proxy calls POST /resellers/v6/catalog/priceandavailability with body {products:[{ingramPartNumber,quantity}]}. Returns pricing, stock by warehouse, discounts. Note: this is a POST, not GET — the console was logging it incorrectly before the audit fix.
  • 4
    Order creationWire POST /api/ingram/orders → proxy calls POST /resellers/v6/orders. Required body fields: customerOrderNumber, shipToInfo, lines[]. The proxy must also include IM-CustomerNumber, IM-CountryCode, and IM-CorrelationID headers on every call.
  • 5
    Order trackingWire GET /api/ingram/orders/{id} → proxy calls GET /resellers/v6.1/orders/{orderNumber}. Map the response's shipmentDetails and orderStatus into the step-timeline format the tracking pane expects.
C4
WIRE TD SYNNEX
  • 1
    SOAP client in proxyAdd a SOAP client library to your Function App (e.g. node-soap for Node.js). Load the WSDL from https://ws.synnex.com/webservice/pnaserviceV05?wsdl. Authenticate with your EC Express customer number and password (stored in Key Vault).
  • 2
    P&A (Price and Availability)Wire POST /api/synnex/pricing → proxy calls pnaserviceV05.getPriceAndAvailability({skus:[]}). Parse the XML response — each item has <price>, <qty>, <status>. Return normalized JSON matching the Ingram P&A shape so the BOM optimizer can compare them directly.
  • 3
    Order submissionWire POST /api/synnex/orders → proxy calls poserviceV02.submitPO({...}). SOAP PO body includes: customer number, PO number, ship-to info, line items with vendor part numbers, quantities. Returns an order confirmation number.
  • 4
    Order statusWire GET /api/synnex/orders/{id} → proxy calls posserviceV02.getPOStatus({poNumber}). Returns shipment status, carrier, tracking number. Map to the same step-timeline format used by the Ingram tracking response.
  • 5
    Cloud licenses (StreamOne Ion)Wire POST /api/synnex/licenses → proxy calls POST https://ion.tdsynnex.com/api/v3/accounts/{id}/subscriptions with Bearer token from StreamOne Ion. This path is only for SaaS products (Microsoft 365, Acronis, etc.) — not physical hardware.
C5
PROXY ACTIVATION CHECKLIST
  • PROXY_BASE set. Updated in the dashboard's script block to your Function App URL (or left empty if same-origin).
  • DEMO_MODE = false during testing. Set to false so fetch errors appear in browser console. Return to true for production if you want silent fallback.
  • Ingram credentials in Key Vault. Application Settings show @Microsoft.KeyVault(...) reference strings for ingram-client-id, ingram-client-secret, and ingram-customer-number.
  • SYNNEX EC Express credentials in Key Vault. synnex-customer-number, synnex-ec-username, synnex-ec-password — used for SOAP calls.
  • StreamOne Ion credentials in Key Vault (if wiring cloud products). ion-client-id, ion-client-secret or ion-refresh-token.
  • Product search returns results. Open the Search pane, type "cisco", check DevTools → Network tab. Confirm the proxy call to /api/ingram/catalog returns 200 with a JSON array. Products should appear in the grid.
  • Price check returns live data. Click "Check Pricing" on any product. Confirm POST /api/ingram/pricing returns 200 with price data. Toast should show the actual reseller price from Ingram.
  • Order submission sandbox tested. Set proxy to point at Ingram sandbox (/sandbox/resellers/v6/orders). Submit a test order. Confirm 201 response with a valid order number. Test account: 20-222222 (Ingram sandbox).
  • Tracking returns step data. Enter a known order number and click TRACK. Confirm the timeline updates with real steps from GET /resellers/v6.1/orders/{id}.
  • API call log shows real paths. Open Analytics pane. Confirm all entries in the API Call History log show the corrected /resellers/v6/ paths — not the old /v1/ paths.
C6
TROUBLESHOOT
SYMPTOMCAUSEFIX
Console shows demo data with proxy setapiFetch() caught an error and fell back silentlySet DEMO_MODE = false. Open DevTools → Console. Find the [ProDesk] fetch failed message with the error detail.
Ingram API returns 401OAuth token expired or not sentToken TTL is ~3600s. Your proxy must refresh it before expiry. Check that the Authorization: Bearer {token} header is present on every call. Verify IM-CustomerNumber header is also included.
Ingram API returns 400 on P&A callSending GET instead of POST, or wrong body shapeThe P&A endpoint is POST /resellers/v6/catalog/priceandavailability with a JSON body containing {products:[{ingramPartNumber, quantity}]}. It accepts up to 50 SKUs per request.
SYNNEX SOAP returns parse errorXML namespace issues or wrong WSDL versionUse the exact WSDL URL: https://ws.synnex.com/webservice/pnaserviceV05?wsdl. Confirm your SOAP client loads the WSDL at startup and regenerates stubs if the WSDL changes.
BOM optimizer shows same prices for both vendorsSYNNEX SOAP not connected; proxy returning Ingram prices for bothCheck proxy logs for SOAP call failures. Verify EC Express credentials in Key Vault. Test the SOAP P&A call directly from the proxy with a single known SYNNEX SKU.
Tracking pane shows demo order regardless of inputfetchTracking() still using static allTrackingOrders lookupReplace the static lookup in fetchTracking() with an apiFetch() call. The demo fallback only fires when the proxy returns an error.
API call log shows /v1/ pathsStale path in a code path not yet updatedSearch the source for /v1/. The one remaining instance is in fetchTracking_OLD_REPLACED() — a dead function that is never called. Safe to ignore or delete the entire function.
Intelligence pane never shows live dataAll five intelligence cards require proxy-side analytics — no vendor API backs themBuild the analytics layer in your proxy (historical P&A storage, substitution matrix, client profile config). See Section 13 for the workaround for each card.