LoadViewer Integration - V1

V1 is your Current Version.
Manage your Api Keys.

V1 Details

Description
Initial release supporting data in CSV formats.
Release Date
2025-04-01
Status
Active
Upload Settings (ERP → LoadViewer)
details

Understanding data flow from your ERP to LoadViewer

General guidelines specific to this version of the API.

1. Data Format

LoadViewer expects data from your ERP in a structured CSV format with two special prefix columns:

  • Column 1: Section type — S (Shipment), B (Container), C (Cargo)
  • Column 2: Row type — H (Header), D (Detail)

Structure Example

csv data

S,H,Reference,Hanging Allowed,Use Multiple Containers
S,D,SANDBOX-01,True,False
B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm
B,D,5867,2352,2393,27200,0
C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper
C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,

Explanation

  • S,H,Reference,Hanging Allowed,Use Multiple Containers: Shipment-level headers
  • S,D,SANDBOX-01,True,False: Shipment-level data rows
  • B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm: Container headers
  • B,D,5867,2352,2393,27200,0: Container data rows (can be multiple)
  • C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper: Cargo headers
  • C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,: Cargo data rows (can be multiple)

Expected Fields

We have used the FIXED, Mandatory, and Optional identifiers for the fields. Rules for data values in each of these fields are as follows.
For FIXED fields no other data is accepted. Match the exact upper and lower case. Do not include extra spaces or characters in any such FIXED fields.
For Mandatory fields, the field cannot be blank. Use 0 for numeric fields and meaningful text for string (character) mandatory fields.
For Optional fields, you may leave them blank for text fields and use 0 for numeric values.

Shipment (S) Section:
  • Shipment(S) Header(H) Row - Only one such row per CSV allowed.
    • S : FIXED. Always provide S here. Since this belongs to the Shipment Section.
    • H : FIXED. Always provide H here. Since this is a Header row of the Section.
    • Reference : FIXED
    • Hanging Allowed : FIXED
    • Use Multiple Containers : FIXED
  • Shipment(S) Details(D) Row - Only one such row per CSV allowed.
    • Section = S : FIXED. Always provide S here. Since this belongs to the Shipment Section.
    • Row Type = D : FIXED. Always provide D here. Since this is a Data row of the Section.
    • Reference = SANDBOX-01 : Mandatory, String: MaxLength[50]. Provide a unique shipment/job reference.

      Values starting with SANDBOX are for testing purposes and are not chargeable.
      When using in a production environment, provide a unique reference—such as an Order ID, Proforma Invoice Number, or Job Code. The value should not exceed 50 characters in total.

    • Hanging Allowed = True : Mandatory. Allowed values – True or False.

      This value tells LoadViewer whether placing any cargo over other cargo requires 100% base support (set False) or 70% base support is sufficient (set True).

    • Use Multiple Containers = False : Mandatory. Allowed values – True or False.

      This is useful to control your coin usage for this shipment. When you want the complete cargo to be loaded using the best containers and no unplanned (shut out) cargo should be left in the packing list, set True. If you want only one container to be planned, set False, and your plan may have Shut-Out (unpacked) cargo. During the planning phase—when you are checking the loadability of an SKU before accepting/revising an order—use False. In the execution (consolidation) phase—while creating a CLP (Container Load Plan) to estimate the number of FCL containers required and how much can be shipped LCL—supply the True value here.

Container (B) Section:
  • Container(B) Header(H) Row - Only one such row per CSV allowed.
    • B : FIXED. Always provide B here. Since this belongs to the Container (Bin for you to relate to the Abbreviation B) Section.
    • H : FIXED. Always provide H here. Since this is a Header row of the Section.
    • Length mm : FIXED.
    • Width mm : FIXED.
    • Height mm : FIXED.
    • Max Weight Kg : FIXED
    • Back Margin mm : FIXED
  • Container(B) Details(D) Row - Multilple rows per CSV allowed.
    • Section = B : FIXED. Always provide B here. Since this belongs to the Container Section.
    • Row Type = D : FIXED. Always provide D here. Since this is a Data row of the Section.
    • Length mm = 5867 : Mandatory, Number: Range [250-30000]. Decimals and commas not allowed. Internal Length (Depth) of the Container in Millimeters (mm).
    • Width mm = 2352 : Mandatory, Number: Range [250-3000]. Decimals and commas not allowed. Internal Width of the Container in Millimeters (mm).
    • Height mm = 2393 : Mandatory, Number: Range [250-3000]. Decimals and commas not allowed. Internal Height of the Container in Millimeters (mm).
    • Max Weight Kg = 27200 : Mandatory, Decimal (up to three digits after the decimal). Range [0.001, 50000]. Commas not allowed.

      Maximum Weight that can be stuffed in the Container in Kg (Kilogram).

    • Back Margin mm = 0 : Mandatory, Number: Range [0-200]. Decimals not allowed.

      Sometimes you may wish LoadViewer not to plan the area near the door of the Container. That area can be used by your loading team for placing latches for the safety of unloading staff or for other purposes. A maximum of 20 CM (or 200 mm) can be reserved for such uses.

Cargo (C) Section:
  • Cargo(C) Header(H) Row - Only one such row per CSV allowed.
    • C : FIXED. Always provide C here. Since this belongs to the Cargo Section.
    • H : FIXED. Always provide H here. Since this is a Header row of the Section.
    • Color : FIXED.
    • Length mm, Width mm and Height mm : FIXED.
    • Weight Kg : FIXED.
    • Cartons : FIXED.
    • Additional Cartons : FIXED.
    • Placement : FIXED.
    • Vertical Rotation Allowed : FIXED.
    • SKU per Carton : FIXED.
    • Empty Pallet Height mm : FIXED.
    • Tray Cap mm : FIXED.
    • Tray Height mm : FIXED.
    • Air Space Lenght mm, Air Space Width mm and Air Space Height mm : FIXED.
    • Name of Set : FIXED.
    • Cartons Ratio in the Set : FIXED.
    • Description : FIXED.
    • Destination : FIXED.
    • PO : FIXED.
    • Shipper : FIXED.
  • Cargo(C) Details(D) Row - Multilple rows per CSV allowed.
    • Section = C : FIXED. Always provide C here. Since this belongs to the Cargo Section.
    • Row Type = D : FIXED. Always provide D here. Since this is a Data row of the Section.
    • Color = #008000 : Mandatory, String: MaxLength [9], Hex color code for your cargo.
    • Length mm = 700, Width mm = 500 and Height mm = 300 : Mandatory, Number: Range [10–3000]. Decimals and commas are not allowed. External dimensions of the cargo in millimeters (mm): Length, Width, and Height respectively.
    • Weight Kg = 91.5 : Mandatory, Decimal (up to three digits after the decimal). Range [0.001–1000]. Commas are not allowed.

      Weight of the carton or pallet in kilograms (kg).

    • Cartons = 300 : Mandatory, Number: Range [1–30000]. Decimals and commas are not allowed. Number of desired cartons (or pallets) you want to load into containers.
    • Additional Cartons = 0 : Mandatory, Number: Range [0–30000]. Decimals and commas are not allowed. LoadViewer will try to place number of additional cargo (cartons or pallets) defined here in any remaining space that cannot be filled by the desired quantities specified in the Cartons section.
    • Placement = 0 : Mandatory, Number: Range [0–4]. Defines how the item should be vertically placed inside the container.

      This setting determines the vertical placement of the item, mainly for palletized cargo or goods with specific stacking requirements.

    • Vertical Rotation Allowed = True : Mandatory. Allowed values: True or False. Indicates whether the item can be rotated vertically during load planning.

      When vertical rotation is True, LoadViewer explores all six possible orientations to optimize space inside the container.
      💡 Image showing all six possible rotations :

    • SKU per Carton = 1 : Mandatory, Number: Range[1–10000]. Decimals and commas are not allowed. Represents how many units of the SKU are packed inside one carton or pallet.
    • Empty Pallet Height mm = 0 : Mandatory, Number: Range [90–200] or 0. Use 0 for cartons, and any value between 90 to 200 mm for empty pallet height (i.e., pallet feet only).
    • Tray Cap mm = 0 : Mandatory, Number: Range [0–200]. Represents the top cap height. This does not reserve any extra space above Height mm but adds a line near the top edge of the cargo for visual reference.
    • Tray Height mm = 0 : Mandatory, Number: Range [0–200]. Represents the tray height in mm. This does not reserve any extra space below Height mm but adds a line near the bottom edge of the cargo for visual reference.
    • Air Space Length mm = 0, Air Space Width mm = 0, and Air Space Height mm = 0 : Mandatory, Number: Range [0–20]. This can be used to allow LoadViewer to keep margins around your cargo up to 2 cm (20 mm).
    • Name of Set : Optional, String: MaxLength [1], Range [B–Y]. You can define up to 24 sets per shipment. Values A and Z are reserved. Leave it blank if your cargo does not belong to any set.
    • Cartons Ratio in the Set = 0 : Mandatory, Number: Range [0–100]. Works in conjunction with Name of Set. When Name of Set is defined, this must be greater than 0.
    • Description : Optional, String: MaxLength [60]. You can enter SKU number, item description, or any meaningful value for LoadViewer reports. For ERP integration, this can also serve as a cargo ID to link with ERP entities.
    • Destination = 0 : Mandatory, Number: Range [0–200]. Use 0 when all cargo is for a single destination. This indicates load/unload priority. Lower values are placed closer to the container door for earlier unloading. Higher values are placed deeper inside the container for last unloading. This improves cargo handling efficiency during transit.
    • PO : Optional, String: MaxLength [20]. You can enter the Purchase Order number to appear on LoadViewer reports. LoadViewer uses this to group SKUs of the same PO in the same container, if sizes and quantities allow.
      💡 Practical Use Case – PO (Purchase Order):
    • Shipper : Optional, String: MaxLength [20]. You can define your department or division name for use in LoadViewer reports. LoadViewer uses this to group SKUs of the same shipper in a single container, if sizes and quantities allow. If multiple departments are involved in a PO, LoadViewer prioritizes departments with higher volume cargo to be placed deeper in the container.

Notes:

  • Separator must be a comma (,)
  • All header and detail rows must strictly follow the S/B/C + H/D format.
  • All header rows are case sensitive and must strictly follow the same format as mentioned in the Headers sections above.
  • Always provide a S,H row followed by at least one S,D row
  • The order of sections can be any. But our example mostly reflects: Shipment → Container(s) → Cargo(s)

Tip: Download SANDBOX-01 and modify the sandbox CSV for accuracy and editing in your text editor / Excel / OpenOffice Calc or other editor of your choice.

2. Authentication

The V1 version uses API Keys for authenticating requests from your ERP system.

How it works:

  • Each user is issued a unique API Key and associated Email.
  • Use these credentials to obtain a JWT token using a secure POST request to our token endpoint.
Manage keys
API Key Manager

Business Email Domain Required
API keys are not available for accounts using free email domains (e.g., Gmail, Hotmail, Yahoo).
If you already have a business email, please log in with that account.
Or, update your email to a business address like your_name@yourcompany.com.

To get your token you need to send a POST request with ApiKey and Email to the token url. Refer to code sections below on how to get the token.
Token Url:
token url

POST https://www.loadviewer.com/api/auth/jwt/token

Request Headers:
header

Content-Type: application/json

Request Body:
body

{
  "email": "your.email@yourcompany.com",
  "apikey": "YOUR_API_KEY_HERE"
}

✅ Success response JSON example:
❌ Error response JSON example:

Key Rotation & Security:
  • Keep your API key secure. Never expose it in frontend code or public repositories.
  • You can regenerate (rotate) your API key at any time. Once rotated, the old key becomes invalid immediately.
  • Always use HTTPS to encrypt the API key and token during transmission.
  • Cache the token on the client side and reuse it for future requests. When the token expires, you'll receive:
    Code/Status = 410, Message = "Token has expired. Please call /api/auth/jwt/token to retrieve a new one."
  • Monitor usage and manage rate limits using the counters associated with your API key.

If a request is made without a valid key, the API will respond with HTTP 401 Unauthorized.


Example: Get Token in Different Languages
Change the YOUR_Email and YOUR_API_KEY_HERE variables in code sections with your actual email and the API Key generated in Api Key Manager.
vb6

' === Constants ===
Const LoadViewerTokenURL As String = "https://www.loadviewer.com/api/auth/jwt/token"
Const LoadViewerAPIEmail As String = "YOUR_Email"
Const LoadViewerAPIKey As String = "YOUR_API_KEY_HERE"

' === Get JWT Token ===
' Returns the token string to be passed in to Authorization Header for every call to 
' LoadViewer endpoints like https://www.loadviewer.com/api/integration/v1/upload
Function getJwtToken() As String
    On Error GoTo ErrHandler

    Dim http As Object
    Set http = CreateObject("WinHttp.WinHttpRequest.5.1")

    Dim jsonBody As String
    jsonBody = "{""email"":""" & LoadViewerAPIEmail & """,""apikey"":""" & LoadViewerAPIKey & """}"

    With http
        .SetTimeouts 3000, 3000, 5000, 5000 ' 3 sec connect/send, 5 sec receive/resolve
        .Open "POST", LoadViewerTokenURL, False
        .SetRequestHeader "Content-Type", "application/json"
        .Send jsonBody
    End With

    If http.Status = 200 Then
        getJwtToken = extractTokenFromResponse(http.ResponseText)
    Else
        getJwtToken = "ERROR: " & http.Status & " - " & http.ResponseText
    End If
    Exit Function

ErrHandler:
    getJwtToken = "Exception: " & Err.Description
End Function

' === Extract "token" field from JSON response ===
Function extractTokenFromResponse(jsonText As String) As String
    Dim tokenStart As Long, tokenEnd As Long
    tokenStart = InStr(jsonText, """token"":""")
    If tokenStart = 0 Then
        extractTokenFromResponse = ""
        Exit Function
    End If
    tokenStart = tokenStart + 9
    tokenEnd = InStr(tokenStart, jsonText, """")
    extractTokenFromResponse = Mid(jsonText, tokenStart, tokenEnd - tokenStart)
End Function

Python

import requests
import json

url = 'https://www.loadviewer.com/api/auth/jwt/token'

payload = {
    'email': 'YOUR_Email',
    'apikey': 'YOUR_API_KEY_HERE'
}

headers = {'Content-Type': 'application/json'}

response = requests.post(url, data=json.dumps(payload), headers=headers)

if response.status_code == 200:
    response_data = response.json()
    token = response_data['token']
    print(f'Token: {token}')
else:
    print(f'Error: {response.status_code} - {response.text}')

C#

using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

class Program
{
    static async Task Main()
    {
        var url = "https://www.loadviewer.com/api/auth/jwt/token";
        var email = "YOUR_Email";
        var apiKey = "YOUR_API_KEY_HERE";

        var payload = new
        {
            email = email,
            apikey = apiKey
        };

        var json = System.Text.Json.JsonSerializer.Serialize(payload);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        using var client = new HttpClient();
        var response = await client.PostAsync(url, content);
        var responseBody = await response.Content.ReadAsStringAsync();

        if (response.IsSuccessStatusCode)
        {
            var token = JObject.Parse(responseBody)["token"]?.ToString();
            Console.WriteLine("Token: " + token);
        }
        else
        {
            Console.WriteLine($"Error: {(int)response.StatusCode} - {responseBody}");
        }
    }
}

JavaScript

// Using Fetch API
const url = 'https://www.loadviewer.com/api/auth/jwt/token';
const payload = {
  email: 'YOUR_Email',
  apikey: 'YOUR_API_KEY_HERE'
};

fetch(url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(payload)
})
.then(response => response.json())
.then(data => {
  if (data.token) {
    console.log('Token:', data.token);
  } else {
    console.error('Authentication failed:', data);
  }
})
.catch(error => {
  console.error('Error:', error);
});

Java

import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;

public class AuthExample {
    public static void main(String[] args) {
        String tokenUrl = "https://www.loadviewer.com/api/auth/jwt/token";
        String email = "YOUR_Email";
        String apiKey = "YOUR_API_KEY_HERE";

        String jsonInputString = String.format("{\"email\":\"%s\",\"apikey\":\"%s\"}", email, apiKey);

        try {
            URL url = new URL(tokenUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setDoOutput(true);

            try (OutputStream os = conn.getOutputStream()) {
                byte[] input = jsonInputString.getBytes("utf-8");
                os.write(input, 0, input.length);
            }

            int status = conn.getResponseCode();
            Scanner scanner;

            if (status == 200) {
                scanner = new Scanner(conn.getInputStream(), "utf-8");
                String responseBody = scanner.useDelimiter("\\A").next();
                System.out.println("Token: " + responseBody);
            } else {
                scanner = new Scanner(conn.getErrorStream(), "utf-8");
                String errorResponse = scanner.useDelimiter("\\A").next();
                System.out.println("Error: " + status + " - " + errorResponse);
            }
            scanner.close();
            conn.disconnect();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

F#

open System
open System.Net.Http
open System.Text
open System.Threading.Tasks

let getTokenAsync () =
    async {
        use client = new HttpClient()
        let url = "https://www.loadviewer.com/api/auth/jwt/token"
        let email = "YOUR_Email"
        let apiKey = "YOUR_API_KEY_HERE"
        let payload = sprintf """{ "email": "%s", "apikey": "%s" }""" email apiKey
        let content = new StringContent(payload, Encoding.UTF8, "application/json")

        try
            let! response = client.PostAsync(url, content) |> Async.AwaitTask
            if response.IsSuccessStatusCode then
                let! body = response.Content.ReadAsStringAsync() |> Async.AwaitTask
                printfn "Token: %s" body
            else
                let! error = response.Content.ReadAsStringAsync() |> Async.AwaitTask
                printfn "Error: %d - %s" (int response.StatusCode) error
        with ex ->
            printfn "Exception: %s" ex.Message
    }

[<EntryPoint>]
let main _ =
    getTokenAsync () |> Async.RunSynchronously
    0

Go

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    url := "https://www.loadviewer.com/api/auth/jwt/token"
    payload := map[string]string{
        "email":  "YOUR_Email",
        "apikey": "YOUR_API_KEY_HERE",
    }

    jsonData, err := json.Marshal(payload)
    if err != nil {
        fmt.Println("Error encoding JSON:", err)
        return
    }

    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
    if err != nil {
        fmt.Println("Error creating request:", err)
        return
    }
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Error making request:", err)
        return
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("Error reading response:", err)
        return
    }

    if resp.StatusCode == http.StatusOK {
        var result map[string]interface{}
        if err := json.Unmarshal(body, &result); err != nil {
            fmt.Println("Error decoding JSON:", err)
            return
        }
        fmt.Println("Token:", result["token"])
    } else {
        fmt.Printf("Error: %d - %s\n", resp.StatusCode, string(body))
    }
}

R

library(httr)
library(jsonlite)

url <- "https://www.loadviewer.com/api/auth/jwt/token"
payload <- list(
  email = "YOUR_Email",
  apikey = "YOUR_API_KEY_HERE"
)

response <- POST(
  url,
  add_headers(`Content-Type` = "application/json"),
  body = toJSON(payload, auto_unbox = TRUE)
)

if (status_code(response) == 200) {
  token <- content(response, as = "parsed")$token
  cat("Token:", token, "\n")
} else {
  cat("Error:", status_code(response), "-", content(response, as = "text"), "\n")
}

PHP

<?php
$tokenUrl = 'https://www.loadviewer.com/api/auth/jwt/token';
$email = 'YOUR_Email';
$apiKey = 'YOUR_API_KEY_HERE';

$data = json_encode(['email' => $email, 'apikey' => $apiKey]);

$ch = curl_init($tokenUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode === 200) {
    $responseData = json_decode($response, true);
    echo "Token: " . $responseData['token'];
} else {
    echo "Error: $httpCode - $response";
}
?>

Dart

import 'dart:convert';
import 'package:http/http.dart' as http;

void main() async {
  var url = Uri.parse('https://www.loadviewer.com/api/auth/jwt/token');
  var headers = {'Content-Type': 'application/json'};
  var body = jsonEncode({
    'email': 'YOUR_Email',
    'apikey': 'YOUR_API_KEY_HERE'
  });

  var response = await http.post(url, headers: headers, body: body);

  if (response.statusCode == 200) {
    var responseData = jsonDecode(response.body);
    print('Token: \${responseData['token']}');
  } else {
    print('Error: \${response.statusCode} - \${response.body}');
  }
}


Token Fetch Errors

These errors may occur while requesting a JWT token using your email and API key. They usually relate to key configuration or mismatch.

Status / code ResponseText / message Solution
400 Missing API Key The apiKey field is missing in the request body. Ensure both email and apiKey are provided.
401 Your domain is not Allowed Your email domain is not permitted. Please use a business email (not a free domain like gmail.com). Contact support if unsure.
401 API Key mismatch The API Key provided does not match our records. Double-check the key and try again.
403 API Key is not generated Generate an API Key first using the API Key Manager before making requests.
403 API Key is cleared, please regenerate Your existing API Key was cleared manually. Please regenerate it from the API Key Manager.
403 API Key is disabled The API Key has been disabled. Enable it again from the API Key Manager to proceed.
403 API Key is Expired The API Key has expired. Please generate a new one from the API Key Manager.

3. Error handling

API Error Handling Guide

Every API response includes a standard structure for handling errors: LoadViewer response messages are in JSON format. But if your application has limitied capabilities parsing JSON you may get the response in Plain text.a
To get the Plain Text responses please add Accept header in the http request headers.

Setting Plain Text Accept Header example:
✅ Success JSON format example:
❌ Error JSON format example:
✅ Success Plain Text example:
❌ Error Plain Text example:

Note: Always check the Status / code and ResponseText / message for handling logic on the ERP side. Refer to the suggested Solution below for each error.

1. Errors related to Upload of CSV data to LoadViewer

These errors occur due to missing headers, invalid, expired, or tampered JWT tokens.

Status / code ResponseText / message Solution
400 Missing 'email' in request body. For token request email and APIKey is not present in the request header.
400 Invalid JSON format. For token request email and APIKey is supplied in proper format in the request header.
400 One or more errors occurred while parsing the CSV. CSV file has errors in column names or data type or out of range etc. Specific details are available in errors section of the JSON.
400 Field 'Reference' is missing in Shipment Row. Mandatory field for shipment is not supplied. Check your CSV file.
400 Shipment must contain at least one container and one cargo item. Your CSV data is missing the Row with 'C' in first column and 'H' in second column.
409 Shipment Number (Field 'Reference') '[XXXXXX]' is used by other Shipment. Check your CSV file and change the Shipment Reference value to not previously used on LoadViewer.
401 Missing or invalid JWT token. Your request is missing a token or has an invalid format. Please obtain a valid token using your credentials.
401 Invalid token. Ensure you are sending a valid Bearer token. Your request is missing a token or has an invalid format. Please obtain a valid token using your credentials.
403 Your domain is not allowed. Your email domain is not allowed. LoadViewer expects a real domain other then gmail.com or hotmail.com etc. Please contact support for more details.
403 Insufficent permissions to perform Upload Action The user has no permission to the resource.
410 Token has expired. Please call /api/auth/jwt/token to retrieve a new one. The token has expired. Please re-authenticate to obtain a new token.
429 Too Many Requests You are exceeding the allowed request rate. Implement retry logic with exponential backoff.

2. Internal & System Errors

These are unexpected system-side issues that may occur temporarily due to backend failures.

Status / code ResponseText / message Solution
500 Internal Server Error Retry the request after a few seconds. If the issue persists, contact LoadViewer support with request details.
503 Service Unavailable The system is temporarily overloaded or under maintenance. Retry after a short delay.

4. Testing

🧪 API Testing Guide

Why Testing is Important

Testing ensures that:

  • Your ERP generates a valid file format
  • Communication with LoadViewer APIs is secure and successful
  • You avoid unnecessary coin consumption during testing.
  • Real-time errors are handled before going live

Your Two-Stage Testing Strategy
  1. Test the File Creation

    Use the manual file upload section in your dashboard to upload a CSV file created by your ERP or a text editor.

    • Make sure the Reference does not start with SANDBOX in manual upload mode.
    • Manual uploads are not charged coins unless published.
    • If the upload is successful, your file structure is correct.
    • Repeat this until your ERP consistently generates acceptable files.
  2. Test the API Communication

    Use your tool of choice (Postman, Swagger, curl, script) to post the file to LoadViewer’s API endpoint.

    • Use Reference that starts with SANDBOX.
    • No coins are consumed for such files. Reference starting with SANDBOX can be provided in mixed case (Upper, Lower, camelCase or else).
    • File is accepted and validated but not published.
    • This lets you test end-to-end without impacting live shipments.

    E.g., SANDBOX-01 is a valid test reference for your dummy data.


Tools & Skills Required
Skill Description
Basic HTTP Understanding Headers, POST, GET, Status Codes
JSON/CSV Correct formatting for request body and payload
Tool usage Postman, curl, Swagger, or scripting languages

ERP Integration Testing
  • Ensure your ERP can generate shipment files in valid CSV format
  • Configure the ERP to send a POST request to:
    https://www.loadviewer.com/api/integration/v1/upload
  • Include ApiKey in the request headers
  • Use a Shipment Reference starting with SANDBOX for test uploads
  • Enable logging of request payloads and API responses within the ERP
  • Test various combinations of SKUs and file sizes before going live

For detailed integration steps, refer to the upcoming Implementation Guide.


Test Code to Upload your CSV data

Note: The example uses integration version V1.

vb6


' ========================================
' Module: modLoadViewerCSV
' Purpose:
'   1. Generate LoadViewer-compatible CSV from hardcoded or database values
'   2. Upload CSV to LoadViewer using HTTP POST (WinHttp)
' Includes helper functions:
'   - removeComma: for all string fields
'   - formatDecimal3: for weights with decimals
' ========================================

' === Constants ===
Const LoadViewerTokenURL As String = "https://www.loadviewer.com/api/auth/jwt/token"
Const LoadViewerUploadURL As String = "https://www.loadviewer.com/api/integration/v1/upload"
Const LoadViewerAPIEmail As String = "YOUR_Email"
Const LoadViewerAPIKey As String = "YOUR_API_KEY_HERE"

' Optional: You can fetch the above values from file or DB:
'   - From INI file in App.Path
'   - From encrypted DB table
'   - From registry or environment variable
'

' === Module level variable to cache the retrieved token ===
Public LoadViewerToken As String 

' === Dynamically generates LoadViewer CSV from ADODB recordsets ===
'For csvGenerate() to work properly you may need to add Reference to
'Microsoft ActiveX Data Object 2.8 Library
'(C:\Program Files (x86)\Common Files\System\ado\msado28.tlb)
'OR any other version for ADODB.Recordset available to you.
Function csvGenerate() As String
    Dim csv As String
    Dim rsShipment As ADODB.Recordset
    Dim rsContainer As ADODB.Recordset
    Dim rsCargo As ADODB.Recordset

    Set rsShipment = New ADODB.Recordset
    Set rsContainer = New ADODB.Recordset
    Set rsCargo = New ADODB.Recordset

    ' --- Define Shipment fields ---
    rsShipment.Fields.Append "Reference", adVarChar, 50
    rsShipment.Fields.Append "HangingAllowed", adBoolean
    rsShipment.Fields.Append "UseMultipleContainers", adBoolean
    rsShipment.Open
    rsShipment.AddNew
    rsShipment("Reference") = "SANDBOX-01"
    rsShipment("HangingAllowed") = True
    rsShipment("UseMultipleContainers") = False
    rsShipment.Update

    ' --- Define Container fields ---
    rsContainer.Fields.Append "Length", adInteger
    rsContainer.Fields.Append "Width", adInteger
    rsContainer.Fields.Append "Height", adInteger
    rsContainer.Fields.Append "MaxWeight", adDouble
    rsContainer.Fields.Append "BackMargin", adInteger
    rsContainer.Open
    rsContainer.AddNew
    rsContainer("Length") = 5867
    rsContainer("Width") = 2352
    rsContainer("Height") = 2393
    rsContainer("MaxWeight") = 27200
    rsContainer("BackMargin") = 0
    rsContainer.Update

    ' --- Define Cargo fields ---
    rsCargo.Fields.Append "Color", adVarChar, 10
    rsCargo.Fields.Append "Length", adInteger
    rsCargo.Fields.Append "Width", adInteger
    rsCargo.Fields.Append "Height", adInteger
    rsCargo.Fields.Append "Weight", adDouble
    rsCargo.Fields.Append "Cartons", adInteger
    rsCargo.Fields.Append "AdditionalCartons", adInteger
    rsCargo.Fields.Append "Placement", adInteger
    rsCargo.Fields.Append "VerticalRotationAllowed", adBoolean
    rsCargo.Fields.Append "SKUPerCarton", adInteger
    rsCargo.Fields.Append "EmptyPalletHeight", adInteger
    rsCargo.Fields.Append "TrayCap", adInteger
    rsCargo.Fields.Append "TrayHeight", adInteger
    rsCargo.Fields.Append "AirSpaceLength", adInteger
    rsCargo.Fields.Append "AirSpaceWidth", adInteger
    rsCargo.Fields.Append "AirSpaceHeight", adInteger
    rsCargo.Fields.Append "NameOfSet", adVarChar, 1
    rsCargo.Fields.Append "CartonRatio", adInteger
    rsCargo.Fields.Append "Description", adVarChar, 60
    rsCargo.Fields.Append "Destination", adInteger
    rsCargo.Fields.Append "PO", adVarChar, 20
    rsCargo.Fields.Append "Shipper", adVarChar, 20
    rsCargo.Open
    rsCargo.AddNew
    rsCargo("Color") = "#008000"
    rsCargo("Length") = 700
    rsCargo("Width") = 500
    rsCargo("Height") = 300
    rsCargo("Weight") = 91.5 ' ? "91.5"
    rsCargo("Cartons") = 300
    rsCargo("AdditionalCartons") = 0
    rsCargo("Placement") = 0
    rsCargo("VerticalRotationAllowed") = True
    rsCargo("SKUPerCarton") = 1
    rsCargo("EmptyPalletHeight") = 0
    rsCargo("TrayCap") = 0
    rsCargo("TrayHeight") = 0
    rsCargo("AirSpaceLength") = 0
    rsCargo("AirSpaceWidth") = 0
    rsCargo("AirSpaceHeight") = 0
    rsCargo("NameOfSet") = ""
    rsCargo("CartonRatio") = 0
    rsCargo("Description") = ""
    rsCargo("Destination") = 0
    rsCargo("PO") = ""
    rsCargo("Shipper") = ""
    rsCargo.Update


    ' --- Begin CSV output ---
    csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers" & vbCrLf
    csv = csv & "S,D," & removeComma(rsShipment("Reference")) & "," & rsShipment("HangingAllowed") & "," & rsShipment("UseMultipleContainers") & vbCrLf
    csv = csv & "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm" & vbCrLf

    Do Until rsContainer.EOF
        csv = csv & "B,D," & rsContainer("Length") & "," & rsContainer("Width") & "," & rsContainer("Height") & "," & formatDecimal3(rsContainer("MaxWeight")) & "," & rsContainer("BackMargin") & vbCrLf
        rsContainer.MoveNext
    Loop

    csv = csv & "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper" & vbCrLf

    Do Until rsCargo.EOF
        csv = csv & "C,D," & removeComma(rsCargo("Color")) & "," & rsCargo("Length") & "," & rsCargo("Width") & "," & rsCargo("Height") & "," & formatDecimal3(rsCargo("Weight")) & "," & rsCargo("Cartons") & "," & rsCargo("AdditionalCartons") & "," & rsCargo("Placement") & "," & rsCargo("VerticalRotationAllowed") & "," & rsCargo("SKUPerCarton") & "," & rsCargo("EmptyPalletHeight") & "," & rsCargo("TrayCap") & "," & rsCargo("TrayHeight") & "," & rsCargo("AirSpaceLength") & "," & rsCargo("AirSpaceWidth") & "," & rsCargo("AirSpaceHeight") & "," & removeComma(rsCargo("NameOfSet")) & "," & rsCargo("CartonRatio") & "," & removeComma(rsCargo("Description")) & "," & rsCargo("Destination") & "," & removeComma(rsCargo("PO")) & "," & removeComma(rsCargo("Shipper")) & vbCrLf
        rsCargo.MoveNext
    Loop

    csvGenerate = csv
End Function

' === Generate CSV string for SANDBOX-01 ===
Function csvCreate() As String
    Dim csv As String
    csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers" & vbCrLf
    csv = csv & "S,D,SANDBOX-01,True,False" & vbCrLf
    csv = csv & "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm" & vbCrLf
    csv = csv & "B,D,5867,2352,2393,27200,0" & vbCrLf
    csv = csv & "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper" & vbCrLf
    csv = csv & "C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,," & vbCrLf
    csvCreate = csv
End Function

' === Get JWT Token ===
Function getJwtToken() As String
    On Error GoTo ErrHandler

    Dim http As Object
    Set http = CreateObject("WinHttp.WinHttpRequest.5.1")

    Dim tokenURL As String
    tokenURL = LoadViewerTokenURL ' Example: "https://www.loadviewer.com/api/auth/jwt/token"

    Dim jsonBody As String
    jsonBody = "{""email"":""" & LoadViewerAPIEmail & """,""apikey"":""" & LoadViewerAPIKey & """}"

    With http
        .SetTimeouts 3000, 3000, 5000, 5000 ' 3 sec connect/send, 5 sec receive/resolve
        .Open "POST", tokenURL, False
        .SetRequestHeader "Content-Type", "application/json"
        .Send jsonBody
    End With

    If http.Status = 200 Then
        getJwtToken = extractTokenFromResponse(http.ResponseText)    ' you Should extract the token from returned JSON with token
        LoadViewerToken = getJwtToken ' you should cache it for later use to avoid netwroks calls
    Else
        getJwtToken = "ERROR: " & http.Status & " - " & http.ResponseText
    End If

    Exit Function

ErrHandler:
    getJwtToken = "Exception: " & Err.Description
End Function

' === Extract "token" field from JSON response ===
Function extractTokenFromResponse(jsonText As String) As String
    Dim tokenStart As Long, tokenEnd As Long
    tokenStart = InStr(jsonText, """token"":""")
    If tokenStart = 0 Then
        extractTokenFromResponse = ""
        Exit Function
    End If
    tokenStart = tokenStart + 9
    tokenEnd = InStr(tokenStart, jsonText, """")
    extractTokenFromResponse = Mid(jsonText, tokenStart, tokenEnd - tokenStart)
End Function

' === Upload CSV using Bearer Token ===
Function UploadTOLoadViewer(csvData As String, jwtToken As String) As String

Dim retry As Integer
retry = 3

UploadTOLoadViewer:
    Dim http As Object
    Set http = CreateObject("WinHttp.WinHttpRequest.5.1")

    On Error GoTo ErrHandler

    With http
        .Open "POST", LoadViewerUploadURL, False
        .SetRequestHeader "Content-Type", "text/csv"
        .SetRequestHeader "Authorization", "Bearer " & jwtToken
        '.SetRequestHeader "Accept", "text/plain" '->Hint: Comment this line to get JSON format response
        .Send csvData
    End With

    If http.Status = 200 Or http.Status = 202 Then 'Success (200) or Accepted (202)
        UploadTOLoadViewer = http.Status & " - " & http.ResponseText
    ElseIf http.Status = 410 And retry > 0 Then
        getJwtToken
        jwtToken = LoadViewerToken
        retry = retry - 1
        GoTo UploadTOLoadViewer
    Else
        UploadTOLoadViewer = "ERROR: " & http.Status & " - " & http.ResponseText
    End If
    Exit Function
ErrHandler:
    UploadTOLoadViewer = "Exception: " & Err.Description
End Function

' === Cleans text for CSV: removes commas and replaces line breaks ===
' === Use this for all adVarChar (string) fields before writing to CSV ===
'
' Replaces:
'   ,   ?   (removed)
'   vbCrLf, vbCr, vbLf ? " " (space)
Function removeComma(value As Variant) As String
    If IsNull(value) Then
        removeComma = ""
        Exit Function
    End If

    Dim s As String
    s = Trim(CStr(value))

    s = Replace(s, ",", "")                          ' Remove commas
    s = Replace(s, vbCrLf, " ")                      ' Replace Windows line breaks with space
    s = Replace(s, vbCr, " ")                        ' Replace carriage return
    s = Replace(s, vbLf, " ")                        ' Replace line feed

    removeComma = s
End Function

' === Converts numeric value to string with up to 3 decimal places ===
' === Trailing zeros are removed. Use only for Weight-related fields ===
'
' Examples:
'   Convert 91.5       ? "91.5"
'   Convert 91.56789   ? "91.568"
'   Keep 52            ? "52"   (no .000 added)
Function formatDecimal3(value As Variant) As String
    If IsNull(value) Or Not IsNumeric(value) Then
        formatDecimal3 = "0"
        Exit Function
    End If

    Dim s As String
    s = Trim(CStr(value))

    If InStr(s, ".") > 0 Then
        ' Format with 3 decimals, remove trailing zeroes
        formatDecimal3 = Trim(Str(Val(FormatNumber(value, 3, , , vbFalse))))
    Else
        ' Keep as integer string
        formatDecimal3 = Trim(Str(Val(value)))
    End If
End Function

' === OPTIONAL TEST CALL ===
' Make calls Examples
'    testUpload '->SaveToDisk=No, CSVFromADO=No
'    testUpload True '->SaveToDisk=Yes, CSVFromADO=No
'    testUpload False, True '->SaveToDisk=No, CSVFromADO=Yes
'    testUpload True, True '->SaveToDisk=Yes, CSVFromADO=Yes
'===============================================================
Sub testUpload(Optional saveToDisk As Boolean = False, Optional useADORecordset As Boolean = False)
    Dim csv As String
    If (useADORecordset = False) Then
        csv = csvCreate() '--Using Hard Coded values
    Else
        csv = csvGenerate() '-->Using ADO
    End If

    If saveToDisk Then
        filePath = App.Path & "\SANDBOX-VB6.csv"
        Dim fso As Object
        Set fso = CreateObject("Scripting.FileSystemObject")
        Dim fileOut As Object
        Set fileOut = fso.CreateTextFile(filePath, True)
        fileOut.Write csv
        fileOut.Close
    End If

    Dim token As String
    If LoadViewerToken = "" Then
        token = Trim(getJwtToken())
    Else
        token = LoadViewerToken
    End If
    If token = "" Or Left(token, 5) = "ERROR" Or Left(token, 9) = "Exception" Then
        MsgBox "Token Error or Server Not Reachable: " & vbCrLf & token, vbCritical
        Exit Sub
    End If
    Dim response As String
    response = UploadTOLoadViewer(csv, token)
    MsgBox response
End Sub

python

# ========================================
# Module: loadviewer_csv.py
# Purpose:
#   1. Generate LoadViewer-compatible CSV from hardcoded or DB values
#   2. Upload CSV to LoadViewer using HTTP POST
# Includes helper functions:
#   - remove_comma: for all string fields
#   - format_decimal_3: for weights with decimals
# ========================================

import json
from io import StringIO
import requests

# === Configuration Constants ===
# Replace with your actual credentials and endpoints
LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token"
LoadViewerUploadURL = "https://www.loadviewer.com/api/upload"
LoadViewerAPIEmail = "YOUR_Email"
LoadViewerAPIKey = "YOUR_API_KEY_HERE"

# === Global Token Cache ===
loadviewer_token = None

# === Helper Functions ===

def remove_comma(value):
    """
    Cleans text for CSV: removes commas and replaces line breaks.
    Use this for all string fields before writing to CSV.
    """
    if value is None:
        return ""
    s = str(value).strip()
    s = s.replace(",", "")
    s = s.replace("\r\n", " ").replace("\r", " ").replace("\n", " ")
    return s

def format_decimal_3(value):
    """
    Converts numeric value to string with up to 3 decimal places.
    Trailing zeros are removed. Use only for weight-related fields.
    """
    try:
        num = float(value)
        formatted = f"{num:.3f}".rstrip("0").rstrip(".")
        return formatted
    except (TypeError, ValueError):
        return "0"

def extract_token_from_response(json_text):
    """
    Extracts the 'token' field from JSON response.
    """
    try:
        data = json.loads(json_text)
        return data.get("token", "")
    except json.JSONDecodeError:
        return ""

# === CSV Generation Functions ===

def csv_create():
    """
    Generate CSV string for SANDBOX-01 using hardcoded values.
    """
    csv = ""
    csv += "S,H,Reference,Hanging Allowed,Use Multiple Containers\n"
    csv += "S,D,SANDBOX-01,True,False\n"
    csv += "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n"
    csv += "B,D,5867,2352,2393,27200,0\n"
    csv += ("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,"
            "Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,"
            "Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,"
            "Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,"
            "Description,Destination,PO,Shipper\n")
    csv += ("C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,\n")
    return csv

def csv_generate():
    """
    Dynamically generates LoadViewer CSV from simulated recordsets.
    """
    # --- Sample Recordsets simulated with Python dictionaries/lists ---

    # Shipment record (only one row)
    rs_shipment = {
        "Reference": "SANDBOX-01",
        "HangingAllowed": True,
        "UseMultipleContainers": False
    }

    # Container recordset (list of dicts)
    rs_container = [{
        "Length": 5867,
        "Width": 2352,
        "Height": 2393,
        "MaxWeight": 27200,
        "BackMargin": 0
    }]

    # Cargo recordset (list of dicts)
    rs_cargo = [{
        "Color": "#008000",
        "Length": 700,
        "Width": 500,
        "Height": 300,
        "Weight": 91.5,
        "Cartons": 300,
        "AdditionalCartons": 0,
        "Placement": 0,
        "VerticalRotationAllowed": True,
        "SKUPerCarton": 1,
        "EmptyPalletHeight": 0,
        "TrayCap": 0,
        "TrayHeight": 0,
        "AirSpaceLength": 0,
        "AirSpaceWidth": 0,
        "AirSpaceHeight": 0,
        "NameOfSet": "",
        "CartonRatio": 0,
        "Description": "",
        "Destination": 0,
        "PO": "",
        "Shipper": ""
    }]

    # --- CSV construction ---
    output = StringIO()

    output.write("S,H,Reference,Hanging Allowed,Use Multiple Containers\n")
    output.write(f"S,D,{remove_comma(rs_shipment['Reference'])},{rs_shipment['HangingAllowed']},{rs_shipment['UseMultipleContainers']}\n")

    output.write("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n")
    for container in rs_container:
        output.write(f"B,D,{container['Length']},{container['Width']},{container['Height']},{format_decimal_3(container['MaxWeight'])},{container['BackMargin']}\n")

    output.write("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n")

    for cargo in rs_cargo:
        output.write("C,D,")
        output.write(",".join([
            remove_comma(cargo["Color"]),
            str(cargo["Length"]),
            str(cargo["Width"]),
            str(cargo["Height"]),
            format_decimal_3(cargo["Weight"]),
            str(cargo["Cartons"]),
            str(cargo["AdditionalCartons"]),
            str(cargo["Placement"]),
            str(cargo["VerticalRotationAllowed"]),
            str(cargo["SKUPerCarton"]),
            str(cargo["EmptyPalletHeight"]),
            str(cargo["TrayCap"]),
            str(cargo["TrayHeight"]),
            str(cargo["AirSpaceLength"]),
            str(cargo["AirSpaceWidth"]),
            str(cargo["AirSpaceHeight"]),
            remove_comma(cargo["NameOfSet"]),
            str(cargo["CartonRatio"]),
            remove_comma(cargo["Description"]),
            str(cargo["Destination"]),
            remove_comma(cargo["PO"]),
            remove_comma(cargo["Shipper"])
        ]))
        output.write("\n")

    return output.getvalue()

# === Extract "token" field from JSON response ===
def extract_token_from_response(json_text: str) -> str:
    try:
        data = json.loads(json_text)
        return data.get("token", "")
    except json.JSONDecodeError:
        return ""

# === Get JWT Token ===
def get_jwt_token() -> str:
    global loadviewer_token

    try:
        # Return cached token if available
        if loadviewer_token:
            return loadviewer_token

        json_body = {
            "email": LoadViewerAPIEmail,
            "apikey": LoadViewerAPIKey
        }

        response = requests.post(
            LoadViewerTokenURL,
            json=json_body,
            timeout=(3, 5)  # connect, read timeout
        )

        if response.status_code == 200:
            token = extract_token_from_response(response.text)
            loadviewer_token = token  # Cache it
            return token
        else:
            return f"ERROR: {response.status_code} - {response.text}"

    except Exception as e:
        return f"Exception: {str(e)}"


# === Upload CSV using Bearer Token ===
def upload_to_loadviewer(csv_data: str, jwt_token: str) -> str:
    retries = 3
    global loadviewer_token  # reuse cached token

    while retries > 0:
        try:
            headers = {
                "Content-Type": "text/csv",
                "Authorization": f"Bearer {jwt_token}"
                # "Accept": "text/plain"  # Optional: Uncomment if plain text is preferred
            }

            response = requests.post(
                LoadViewerUploadURL,
                data=csv_data.encode("utf-8"),  # Ensure encoding
                headers=headers,
                timeout=(3, 5)  # connect, read
            )

            if response.status_code in (200, 202):  # Success
                return f"{response.status_code} - {response.text}"

            elif response.status_code == 410 and retries > 1:
                # Token expired, refresh it
                jwt_token = get_jwt_token()
                loadviewer_token = jwt_token
                retries -= 1
                continue

            else:
                return f"ERROR: {response.status_code} - {response.text}"

        except Exception as e:
            return f"Exception: {str(e)}"

    return "ERROR: Max retries exceeded"


#=== test call ====
def test_upload(save_to_disk=False, use_ado_recordset=False):
    """
    Test function to generate CSV data and upload it to LoadViewer.
    Can save the CSV to disk and/or use ADO recordset simulation.
    """
    # Generate CSV data
    if not use_ado_recordset:
        csv_data = csv_create()  # Using hardcoded values
    else:
        csv_data = csv_generate()  # Using simulated ADO recordsets

    # Save to disk if required
    if save_to_disk:
        file_path = "SANDBOX-Python.csv"
        with open(file_path, "w", newline="") as file_out:
            file_out.write(csv_data)

    # Retrieve JWT token
    global loadviewer_token
    if not loadviewer_token:
        token = get_jwt_token()
    else:
        token = loadviewer_token

    if not token or token.startswith(("ERROR", "Exception")):
        print(f"Token Error or Server Not Reachable: {token}")
        return

    # Upload CSV data
    response = upload_to_loadviewer(csv_data, token)
    print(response)

csharp

// ========================================
// Module: modLoadViewerCSV
// Purpose:
//     1. Generate LoadViewer-compatible CSV from hardcoded or database values
//     2. Upload CSV to LoadViewer using HTTP POST (WinHttp equivalent in C# is HttpClient)
// Includes helper functions:
//     - removeComma: for all string fields
//     - formatDecimal3: for weights with decimals
// ========================================

using System;
using System.Data.OleDb; // For ADODB recordset equivalent
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

public class LoadViewerCSV
{
    // === Constants ===
    private const string LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token";
    private const string LoadViewerUploadURL = "https://www.loadviewer.com/api/integration/v1/upload";
    private const string LoadViewerAPIEmail = "YOUR_Email";
    private const string LoadViewerAPIKey = "YOUR_API_KEY_HERE";

    // Optional: You can fetch the above values from file or DB:
    //     - From INI file in App.Path (use System.Configuration in C#)
    //     - From encrypted DB table
    //     - From registry or environment variable (use Microsoft.Win32.Registry in C#)
    //

    // === Module level variable to cache the retrieved token ===
    public static string LoadViewerToken { get; private set; }

    // === Dynamically generates LoadViewer CSV from ADODB recordsets ===
    // For csvGenerate() to work properly, you may need to add System.Data.OleDb
    public static string CsvGenerate()
    {
        string csv = "";
        OleDbDataReader rsShipment = null;
        OleDbDataReader rsContainer = null;
        OleDbDataReader rsCargo = null;
        OleDbConnection connection = null;

        try
        {
            // --- Define Shipment fields ---
            string shipmentQuery = "SELECT 'SANDBOX-01' as Reference, true as HangingAllowed, false as UseMultipleContainers";
            string containerQuery = "SELECT 5867 as Length, 2352 as Width, 2393 as Height, 27200 as MaxWeight, 0 as BackMargin";
            string cargoQuery = "SELECT '#008000' as Color, 700 as Length, 500 as Width, 300 as Height, 91.5 as Weight, 300 as Cartons, 0 as AdditionalCartons, 0 as Placement, true as VerticalRotationAllowed, 1 as SKUPerCarton, 0 as EmptyPalletHeight, 0 as TrayCap, 0 as TrayHeight, 0 as AirSpaceLength, 0 as AirSpaceWidth, 0 as AirSpaceHeight, '' as NameOfSet, 0 as CartonRatio, '' as Description, 0 as Destination, '' as PO, '' as Shipper";

            // Create a dummy connection and reader just to simulate vb6 behaviour
            connection = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=.;Extended Properties=dBASE IV;");
            connection.Open();

            OleDbCommand shipmentCommand = new OleDbCommand(shipmentQuery, connection);
            rsShipment = shipmentCommand.ExecuteReader();

            OleDbCommand containerCommand = new OleDbCommand(containerQuery, connection);
            rsContainer = containerCommand.ExecuteReader();

            OleDbCommand cargoCommand = new OleDbCommand(cargoQuery, connection);
            rsCargo = cargoCommand.ExecuteReader();

            // --- Begin CSV output ---
            csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers" + Environment.NewLine;
            if (rsShipment.Read())
            {
                csv += "S,D," + RemoveComma(rsShipment["Reference"].ToString()) + "," + rsShipment["HangingAllowed"] + "," + rsShipment["UseMultipleContainers"] + Environment.NewLine;
            }

            csv += "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm" + Environment.NewLine;
            if (rsContainer.Read())
            {
                csv += "B,D," + rsContainer["Length"] + "," + rsContainer["Width"] + "," + rsContainer["Height"] + "," + FormatDecimal3(rsContainer["MaxWeight"]) + "," + rsContainer["BackMargin"] + Environment.NewLine;
            }

            csv += "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper" + Environment.NewLine;
            if (rsCargo.Read())
            {
                csv += "C,D," + RemoveComma(rsCargo["Color"].ToString()) + "," + rsCargo["Length"] + "," + rsCargo["Width"] + "," + rsCargo["Height"] + "," + FormatDecimal3(rsCargo["Weight"]) + "," + rsCargo["Cartons"] + "," + rsCargo["AdditionalCartons"] + "," + rsCargo["Placement"] + "," + rsCargo["VerticalRotationAllowed"] + "," + rsCargo["SKUPerCarton"] + "," + rsCargo["EmptyPalletHeight"] + "," + rsCargo["TrayCap"] + "," + rsCargo["TrayHeight"] + "," + rsCargo["AirSpaceLength"] + "," + rsCargo["AirSpaceWidth"] + "," + rsCargo["AirSpaceHeight"] + "," + RemoveComma(rsCargo["NameOfSet"].ToString()) + "," + rsCargo["CartonRatio"] + "," + RemoveComma(rsCargo["Description"].ToString()) + "," + rsCargo["Destination"] + "," + RemoveComma(rsCargo["PO"].ToString()) + "," + RemoveComma(rsCargo["Shipper"].ToString()) + Environment.NewLine;
            }
        }
        catch (Exception ex)
        {
            return "Exception: " + ex.Message;
        }
        finally
        {
            if (rsShipment != null) rsShipment.Close();
            if (rsContainer != null) rsContainer.Close();
            if (rsCargo != null) rsCargo.Close();
            if (connection != null) connection.Close();
        }

        return csv;
    }

    // === Generate CSV string for SANDBOX-01 ===
    public static string CsvCreate()
    {
        string csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers" + Environment.NewLine;
        csv += "S,D,SANDBOX-01,True,False" + Environment.NewLine;
        csv += "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm" + Environment.NewLine;
        csv += "B,D,5867,2352,2393,27200,0" + Environment.NewLine;
        csv += "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper" + Environment.NewLine;
        csv += "C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,," + Environment.NewLine;
        return csv;
    }

    // === Get JWT Token ===
    public static async Task<string> GetJwtTokenAsync()
    {
        try
        {
            using (HttpClient client = new HttpClient())
            {
                client.Timeout = TimeSpan.FromSeconds(5); // Adjust timeouts
                var jsonBody = JsonSerializer.Serialize(new { email = LoadViewerAPIEmail, apikey = LoadViewerAPIKey });
                var content = new StringContent(jsonBody, System.Text.Encoding.UTF8, "application/json");
                var response = await client.PostAsync(LoadViewerTokenURL, content);

                if (response.IsSuccessStatusCode)
                {
                    string responseText = await response.Content.ReadAsStringAsync();
                    string token = ExtractTokenFromResponse(responseText);
                    LoadViewerToken = token; // Cache the token
                    return token;
                }
                else
                {
                    return $"ERROR: {(int)response.StatusCode} - {await response.Content.ReadAsStringAsync()}";
                }
            }
        }
        catch (Exception ex)
        {
            return $"Exception: {ex.Message}";
        }
    }

    // === Extract "token" field from JSON response ===
    private static string ExtractTokenFromResponse(string jsonText)
    {
        try
        {
            using (JsonDocument doc = JsonDocument.Parse(jsonText))
            {
                if (doc.RootElement.TryGetProperty("token", out JsonElement tokenElement))
                {
                    return tokenElement.GetString();
                }
            }
        }
        catch { }
        return "";
    }

    // === Upload CSV using Bearer Token ===
    public static async Task<string> UploadToLoadViewerAsync(string csvData, string jwtToken)
    {
        int retry = 3;
        while (retry > 0)
        {
            try
            {
                using (HttpClient client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", jwtToken);
                    var content = new StringContent(csvData, System.Text.Encoding.UTF8, "text/csv");
                    var response = await client.PostAsync(LoadViewerUploadURL, content);

                    if (response.StatusCode == System.Net.HttpStatusCode.OK || response.StatusCode == System.Net.HttpStatusCode.Accepted)
                    {
                        return $"{(int)response.StatusCode} - {await response.Content.ReadAsStringAsync()}";
                    }
                    else if (response.StatusCode == System.Net.HttpStatusCode.Gone) // 410
                    {
                        jwtToken = await GetJwtTokenAsync(); // Refresh token
                        if (jwtToken.StartsWith("ERROR") || jwtToken.StartsWith("Exception"))
                        {
                            return jwtToken; // Return error from token retrieval
                        }
                        retry--;
                        continue; // Retry upload
                    }
                    else
                    {
                        return $"ERROR: {(int)response.StatusCode} - {await response.Content.ReadAsStringAsync()}";
                    }
                }
            }
            catch (Exception ex)
            {
                return $"Exception: {ex.Message}";
            }
        }
        return "Upload failed after retries.";
    }

    // === Cleans text for CSV: removes commas and replaces line breaks ===
    // === Use this for all adVarChar (string) fields before writing to CSV ===
    //
    // Replaces:
    //     ,     ?     (removed)
    //     vbCrLf, vbCr, vbLf ? " " (space)
    public static string RemoveComma(string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            return "";
        }

        string s = value.Trim();
        s = s.Replace(",", "");
        s = s.Replace(Environment.NewLine, " ");
        s = s.Replace("\r", " ");
        s = s.Replace("\n", " ");

        return s;
    }

    // === Converts numeric value to string with up to 3 decimal places ===
    // === Trailing zeros are removed. Use only for Weight-related fields ===
    //
    // Examples:
    //     Convert 91.5      ? "91.5"
    //     Convert 91.56789  ? "91.568"
    //     Keep 52           ? "52"    (no .000 added)
    public static string FormatDecimal3(double value)
    {
        return value.ToString("0.###");
    }

    // === OPTIONAL TEST CALL ===
    // Make calls Examples
    //     TestUploadAsync() //->SaveToDisk=No, CSVFromADO=No
    //     TestUploadAsync(true) //->SaveToDisk=Yes, CSVFromADO=No
    //     TestUploadAsync(false, true) //->SaveToDisk=No, CSVFromADO=Yes
    //     TestUploadAsync(true, true) //->SaveToDisk=Yes, CSVFromADO=Yes
    //===============================================================
    public static async Task TestUploadAsync(bool saveToDisk = false, bool useADORecordset = false)
    {
        string csv;
        if (!useADORecordset)
        {
            csv = CsvCreate(); //--Using Hard Coded values
        }
        else
        {
            csv = CsvGenerate(); //-->Using ADO
        }

        if (saveToDisk)
        {
            string filePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SANDBOX-VB6.csv");
            System.IO.File.WriteAllText(filePath, csv);
        }

        string token;
        if (string.IsNullOrEmpty(LoadViewerToken))
        {
            token = await GetJwtTokenAsync();
        }
        else
        {
            token = LoadViewerToken;
        }

        if (string.IsNullOrEmpty(token) || token.StartsWith("ERROR") || token.StartsWith("Exception"))
        {
            Console.WriteLine("Token Error or Server Not Reachable: " + Environment.NewLine + token);
            return;
        }

        string response = await UploadToLoadViewerAsync(csv, token);
        Console.WriteLine(response);
    }
}
js

// ========================================
// Module: modLoadViewerCSV
// Purpose:
//     1. Generate LoadViewer-compatible CSV from hardcoded or database values
//     2. Upload CSV to LoadViewer using HTTP POST (fetch API)
// Includes helper functions:
//     - removeComma: for all string fields
//     - formatDecimal3: for weights with decimals
// ========================================

// === Constants ===
const LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token";
const LoadViewerUploadURL = "https://www.loadviewer.com/api/integration/v1/upload";
const LoadViewerAPIEmail = "YOUR_Email";
const LoadViewerAPIKey = "YOUR_API_KEY_HERE";

// Optional: You can fetch the above values from file or DB:
//     - From local storage, or from a server config file
//     - From encrypted DB table (Node.js with a database library)
//     - From environment variable (process.env in Node.js)
//

// === Module level variable to cache the retrieved token ===
let LoadViewerToken = "";

// === Dynamically generates LoadViewer CSV from hardcoded data ===
function csvGenerate() {
    let csv = "";
    const shipmentData = {
        Reference: "SANDBOX-01",
        HangingAllowed: true,
        UseMultipleContainers: false,
    };

    const containerData = {
        Length: 5867,
        Width: 2352,
        Height: 2393,
        MaxWeight: 27200,
        BackMargin: 0,
    };

    const cargoData = {
        Color: "#008000",
        Length: 700,
        Width: 500,
        Height: 300,
        Weight: 91.5,
        Cartons: 300,
        AdditionalCartons: 0,
        Placement: 0,
        VerticalRotationAllowed: true,
        SKUPerCarton: 1,
        EmptyPalletHeight: 0,
        TrayCap: 0,
        TrayHeight: 0,
        AirSpaceLength: 0,
        AirSpaceWidth: 0,
        AirSpaceHeight: 0,
        NameOfSet: "",
        CartonRatio: 0,
        Description: "",
        Destination: 0,
        PO: "",
        Shipper: "",
    };

    // --- Begin CSV output ---
    csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers\n";
    csv += `S,D,${removeComma(shipmentData.Reference)},${shipmentData.HangingAllowed},${shipmentData.UseMultipleContainers}\n`;
    csv += "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n";
    csv += `B,D,${containerData.Length},${containerData.Width},${containerData.Height},${formatDecimal3(containerData.MaxWeight)},${containerData.BackMargin}\n`;
    csv += "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n";
    csv += `C,D,${removeComma(cargoData.Color)},${cargoData.Length},${cargoData.Width},${cargoData.Height},${formatDecimal3(cargoData.Weight)},${cargoData.Cartons},${cargoData.AdditionalCartons},${cargoData.Placement},${cargoData.VerticalRotationAllowed},${cargoData.SKUPerCarton},${cargoData.EmptyPalletHeight},${cargoData.TrayCap},${cargoData.TrayHeight},${cargoData.AirSpaceLength},${cargoData.AirSpaceWidth},${cargoData.AirSpaceHeight},${removeComma(cargoData.NameOfSet)},${cargoData.CartonRatio},${removeComma(cargoData.Description)},${cargoData.Destination},${removeComma(cargoData.PO)},${removeComma(cargoData.Shipper)}\n`;

    return csv;
}

// === Generate CSV string for SANDBOX-01 ===
function csvCreate() {
    let csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers\n";
    csv += "S,D,SANDBOX-01,True,False\n";
    csv += "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n";
    csv += "B,D,5867,2352,2393,27200,0\n";
    csv += "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n";
    csv += "C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,\n";
    return csv;
}

// === Get JWT Token ===
async function getJwtToken() {
    try {
        const response = await fetch(LoadViewerTokenURL, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({ email: LoadViewerAPIEmail, apikey: LoadViewerAPIKey }),
        });

        if (response.ok) {
            const json = await response.json();
            const token = json.token;
            LoadViewerToken = token; // Cache the token
            return token;
        } else {
            return `ERROR: ${response.status} - ${await response.text()}`;
        }
    } catch (error) {
        return `Exception: ${error.message}`;
    }
}

// === Upload CSV using Bearer Token ===
async function UploadToLoadViewer(csvData, jwtToken) {
    let retry = 3;
    while (retry > 0) {
        try {
            const response = await fetch(LoadViewerUploadURL, {
                method: "POST",
                headers: {
                    "Content-Type": "text/csv",
                    Authorization: `Bearer ${jwtToken}`,
                },
                body: csvData,
            });

            if (response.status === 200 || response.status === 202) {
                return `${response.status} - ${await response.text()}`;
            } else if (response.status === 410) {
                jwtToken = await getJwtToken();
                if (jwtToken.startsWith("ERROR") || jwtToken.startsWith("Exception")) {
                    return jwtToken;
                }
                retry--;
                continue;
            } else {
                return `ERROR: ${response.status} - ${await response.text()}`;
            }
        } catch (error) {
            return `Exception: ${error.message}`;
        }
    }
    return "Upload failed after retries.";
}

// === Cleans text for CSV: removes commas and replaces line breaks ===
// === Use this for all string fields before writing to CSV ===
//
// Replaces:
//     ,     ?     (removed)
//     \r\n, \r, \n ? " " (space)
function removeComma(value) {
    if (value === null || value === undefined) {
        return "";
    }

    let s = String(value).trim();
    s = s.replace(/,/g, "");
    s = s.replace(/\r\n/g, " ");
    s = s.replace(/\r/g, " ");
    s = s.replace(/\n/g, " ");

    return s;
}

// === Converts numeric value to string with up to 3 decimal places ===
// === Trailing zeros are removed. Use only for Weight-related fields ===
//
// Examples:
//     Convert 91.5      ? "91.5"
//     Convert 91.56789  ? "91.568"
//     Keep 52           ? "52"    (no .000 added)
function formatDecimal3(value) {
    if (value === null || value === undefined || isNaN(value)) {
        return "0";
    }

    const strValue = value.toString();
    if (strValue.indexOf('.') === -1) {
        return strValue; // Return integer value as is
    }

    const rounded = parseFloat(value.toFixed(3));
    return rounded.toString();
}

// === OPTIONAL TEST CALL ===
// Make calls Examples
//     testUpload() //-> CSVFromHardCoded
//===============================================================
async function testUpload() {
    const csv = csvCreate(); //--Using Hard Coded values

    let token;
    if (!LoadViewerToken) {
        token = await getJwtToken();
    } else {
        token = LoadViewerToken;
    }

    if (!token || token.startsWith("ERROR") || token.startsWith("Exception")) {
        console.error("Token Error or Server Not Reachable: \n" + token);
        return;
    }

    const response = await UploadToLoadViewer(csv, token);
    console.log(response);
}

java

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;

public class LoadViewerCSV {

    // === Constants ===
    private static final String LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token";
    private static final String LoadViewerUploadURL = "https://www.loadviewer.com/api/integration/v1/upload";
    private static final String LoadViewerAPIEmail = "YOUR_Email";
    private static final String LoadViewerAPIKey = "YOUR_API_KEY_HERE";

    // Optional: You can fetch the above values from file or DB:
    // - From properties file, or from a database config table
    // - From encrypted DB table
    // - From environment variable (System.getenv() in Java)

    // === Module level variable to cache the retrieved token ===
    private static String LoadViewerToken = "";

    // === Dynamically generates LoadViewer CSV from hardcoded data ===
    public static String csvGenerate() {
        StringBuilder csv = new StringBuilder();
        Map<String, Object> shipmentData=new HashMap<>(); shipmentData.put("Reference", "SANDBOX-01");
        shipmentData.put("HangingAllowed", true);
        shipmentData.put("UseMultipleContainers", false);

        Map<String, Object> containerData = new HashMap<>(); containerData.put("Length", 5867);
        containerData.put("Width", 2352);
        containerData.put("Height", 2393);
        containerData.put("MaxWeight", 27200.0);
        containerData.put("BackMargin", 0);

        Map<String, Object> cargoData = new HashMap<>(); cargoData.put("Color", "#008000");
        cargoData.put("Length", 700);
        cargoData.put("Width", 500);
        cargoData.put("Height", 300);
        cargoData.put("Weight", 91.5);
        cargoData.put("Cartons", 300);
        cargoData.put("AdditionalCartons", 0);
        cargoData.put("Placement", 0);
        cargoData.put("VerticalRotationAllowed", true);
        cargoData.put("SKUPerCarton", 1);
        cargoData.put("EmptyPalletHeight", 0);
        cargoData.put("TrayCap", 0);
        cargoData.put("TrayHeight", 0);
        cargoData.put("AirSpaceLength", 0);
        cargoData.put("AirSpaceWidth", 0);
        cargoData.put("AirSpaceHeight", 0);
        cargoData.put("NameOfSet", "");
        cargoData.put("CartonRatio", 0);
        cargoData.put("Description", "");
        cargoData.put("Destination", 0);
        cargoData.put("PO", "");
        cargoData.put("Shipper", "");

        // --- Begin CSV output ---
        csv.append("S,H,Reference,Hanging Allowed,Use Multiple Containers\n");
        csv.append(String.format("S,D,%s,%s,%s\n", removeComma(shipmentData.get("Reference").toString()),
                shipmentData.get("HangingAllowed"), shipmentData.get("UseMultipleContainers")));
        csv.append("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n");
        csv.append(String.format("B,D,%s,%s,%s,%s,%s\n", containerData.get("Length"), containerData.get("Width"),
                containerData.get("Height"), formatDecimal3((Double) containerData.get("MaxWeight")),
                containerData.get("BackMargin")));
        csv.append("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n");
        csv.append(String.format("C,D,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
                removeComma(cargoData.get("Color").toString()), cargoData.get("Length"), cargoData.get("Width"),
                cargoData.get("Height"), formatDecimal3((Double) cargoData.get("Weight")), cargoData.get("Cartons"),
                cargoData.get("AdditionalCartons"), cargoData.get("Placement"), cargoData.get("VerticalRotationAllowed"),
                cargoData.get("SKUPerCarton"), cargoData.get("EmptyPalletHeight"), cargoData.get("TrayCap"),
                cargoData.get("TrayHeight"), cargoData.get("AirSpaceLength"), cargoData.get("AirSpaceWidth"),
                cargoData.get("AirSpaceHeight"), removeComma(cargoData.get("NameOfSet").toString()),
                cargoData.get("CartonRatio"), removeComma(cargoData.get("Description").toString()),
                cargoData.get("Destination"), removeComma(cargoData.get("PO").toString()),
                removeComma(cargoData.get("Shipper").toString())));

        return csv.toString();
    }

    // === Generate CSV string for SANDBOX-01 ===
    public static String csvCreate() {
        StringBuilder csv = new StringBuilder();
        csv.append("S,H,Reference,Hanging Allowed,Use Multiple Containers\n");
        csv.append("S,D,SANDBOX-01,True,False\n");
        csv.append("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n");
        csv.append("B,D,5867,2352,2393,27200,0\n");
        csv.append("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n");
        csv.append("C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,\n");
        return csv.toString();
    }

    // === Get JWT Token ===
    public static String getJwtToken() throws IOException, InterruptedException {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(LoadViewerTokenURL))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(String.format("{\"email\":\"%s\",\"apikey\":\"%s\"}",
                        LoadViewerAPIEmail, LoadViewerAPIKey)))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() == 200) {
            String json = response.body();
            String token = json.substring(json.indexOf("\"token\":\"") + 9, json.indexOf("\"", json.indexOf("\"token\":\"") + 9));
            LoadViewerToken = token; // Cache the token
            return token;
        } else {
            return "ERROR: " + response.statusCode() + " - " + response.body();
        }
    }

    // === Upload CSV using Bearer Token ===
    public static String UploadToLoadViewer(String csvData, String jwtToken) throws IOException, InterruptedException {
        int retry = 3;
        while (retry > 0) {
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(LoadViewerUploadURL))
                    .header("Content-Type", "text/csv")
                    .header("Authorization", "Bearer " + jwtToken)
                    .POST(HttpRequest.BodyPublishers.ofString(csvData))
                    .build();

            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() == 200 || response.statusCode() == 202) {
                return response.statusCode() + " - " + response.body();
            } else if (response.statusCode() == 410) {
                jwtToken = getJwtToken();
                if (jwtToken.startsWith("ERROR")) {
                    return jwtToken;
                }
                retry--;
                continue;
            } else {
                return "ERROR: " + response.statusCode() + " - " + response.body();
            }
        }
        return "Upload failed after retries.";
    }

    // === Cleans text for CSV: removes commas and replaces line breaks ===
    // === Use this for all string fields before writing to CSV ===
    //
    // Replaces:
    // , ? (removed)
    // \r\n, \r, \n ? " " (space)
    public static String removeComma(String value) {
        if (value == null) {
            return "";
        }
        String s = value.trim();
        s = s.replace(",", "");
        s = s.replace("\r\n", " ");
        s = s.replace("\r", " ");
        s = s.replace("\n", " ");
        return s;
    }

    // === Converts numeric value to string with up to 3 decimal places ===
    // === Trailing zeros are removed. Use only for Weight-related fields ===
    //
    // Examples:
    // Convert 91.5 ? "91.5"
    // Convert 91.56789 ? "91.568"
    // Keep 52 ? "52" (no .000 added)
    public static String formatDecimal3(Double value) {
        DecimalFormat df = new DecimalFormat("0.###");
        return df.format(value);
    }

    // === OPTIONAL TEST CALL ===
    // Make calls Examples
    // testUpload() //-> CSVFromHardCoded
    // ===============================================================
    public static void testUpload() throws IOException, InterruptedException {
        String csv = csvCreate(); // --Using Hard Coded values

        String token;
        if (LoadViewerToken.isEmpty()) {
            token = getJwtToken();
        } else {
            token = LoadViewerToken;
        }

        if (token.startsWith("ERROR")) {
            System.err.println("Token Error or Server Not Reachable: \n" + token);
            return;
        }

        String response = UploadToLoadViewer(csv, token);
        System.out.println(response);
    }
}
fsharp

open System
open System.Net.Http
open System.Text.Json
open System.Threading.Tasks

// ========================================
// Module: modLoadViewerCSV
// Purpose:
//     1. Generate LoadViewer-compatible CSV from hardcoded or database values
//     2. Upload CSV to LoadViewer using HTTP POST (HttpClient)
// Includes helper functions:
//     - removeComma: for all string fields
//     - formatDecimal3: for weights with decimals
// ========================================

// === Constants ===
let LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token"
let LoadViewerUploadURL = "https://www.loadviewer.com/api/integration/v1/upload"
let LoadViewerAPIEmail = "YOUR_Email"
let LoadViewerAPIKey = "YOUR_API_KEY_HERE"

// Optional: You can fetch the above values from file or DB:
//     - From config files, or from a database config table
//     - From encrypted DB table
//     - From environment variables (Environment.GetEnvironmentVariable in F#)

// === Module level variable to cache the retrieved token ===
let mutable LoadViewerToken = ""

// === Dynamically generates LoadViewer CSV from hardcoded data ===
let csvGenerate () =
    let shipmentData = 
        [ "Reference", "SANDBOX-01"; "HangingAllowed", true; "UseMultipleContainers", false ] |> Map.ofList

    let containerData = 
        [ "Length", 5867; "Width", 2352; "Height", 2393; "MaxWeight", 27200.0; "BackMargin", 0 ] |> Map.ofList

    let cargoData = 
        [ "Color", "#008000"; "Length", 700; "Width", 500; "Height", 300; "Weight", 91.5; "Cartons", 300; 
          "AdditionalCartons", 0; "Placement", 0; "VerticalRotationAllowed", true; "SKUPerCarton", 1; 
          "EmptyPalletHeight", 0; "TrayCap", 0; "TrayHeight", 0; "AirSpaceLength", 0; "AirSpaceWidth", 0; 
          "AirSpaceHeight", 0; "NameOfSet", ""; "CartonRatio", 0; "Description", ""; "Destination", 0; 
          "PO", ""; "Shipper", "" ] |> Map.ofList

    let csv = StringBuilder()
    csv.Append("S,H,Reference,Hanging Allowed,Use Multiple Containers\n") |> ignore
    csv.Append(sprintf "S,D,%s,%b,%b\n" (removeComma (shipmentData.["Reference"].ToString())) shipmentData.["HangingAllowed"] shipmentData.["UseMultipleContainers"]) |> ignore
    csv.Append("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n") |> ignore
    csv.Append(sprintf "B,D,%i,%i,%i,%s,%i\n" containerData.["Length"] containerData.["Width"] containerData.["Height"] (formatDecimal3 (containerData.["MaxWeight"] :> float)) containerData.["BackMargin"]) |> ignore
    csv.Append("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n") |> ignore
    csv.Append(sprintf "C,D,%s,%i,%i,%i,%s,%i,%i,%i,%b,%i,%i,%i,%i,%i,%i,%i,%s,%i,%s,%i,%s,%s\n" 
                       (removeComma (cargoData.["Color"].ToString())) cargoData.["Length"] cargoData.["Width"] cargoData.["Height"] 
                       (formatDecimal3 (cargoData.["Weight"] :> float)) cargoData.["Cartons"] cargoData.["AdditionalCartons"] cargoData.["Placement"] 
                       cargoData.["VerticalRotationAllowed"] cargoData.["SKUPerCarton"] cargoData.["EmptyPalletHeight"] cargoData.["TrayCap"] 
                       cargoData.["TrayHeight"] cargoData.["AirSpaceLength"] cargoData.["AirSpaceWidth"] cargoData.["AirSpaceHeight"] 
                       (removeComma (cargoData.["NameOfSet"].ToString())) cargoData.["CartonRatio"] (removeComma (cargoData.["Description"].ToString())) 
                       cargoData.["Destination"] (removeComma (cargoData.["PO"].ToString())) (removeComma (cargoData.["Shipper"].ToString()))) |> ignore
    csv.ToString()

// === Generate CSV string for SANDBOX-01 ===
let csvCreate () =
    let csv = StringBuilder()
    csv.Append("S,H,Reference,Hanging Allowed,Use Multiple Containers\n") |> ignore
    csv.Append("S,D,SANDBOX-01,True,False\n") |> ignore
    csv.Append("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n") |> ignore
    csv.Append("B,D,5867,2352,2393,27200,0\n") |> ignore
    csv.Append("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n") |> ignore
    csv.Append("C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,\n") |> ignore
    csv.ToString()

// === Get JWT Token ===
let getJwtToken () =
    task {
        try
            use client = new HttpClient()
            client.Timeout <- TimeSpan.FromSeconds(5.0)
            let jsonBody = JsonSerializer.Serialize([ "email", LoadViewerAPIEmail; "apikey", LoadViewerAPIKey ] |> Map.ofList)
            let content = new StringContent(jsonBody, System.Text.Encoding.UTF8, "application/json")
            let! response = client.PostAsync(LoadViewerTokenURL, content)
            if response.IsSuccessStatusCode then
                let! responseText = response.Content.ReadAsStringAsync()
                let json = JsonDocument.Parse(responseText)
                let token = json.RootElement.GetProperty("token").GetString()
                LoadViewerToken <- token
                return token
            else
                return sprintf "ERROR: %i - %s" (int response.StatusCode) (response.Content.ReadAsStringAsync().Result)
        with ex ->
            return sprintf "Exception: %s" ex.Message
    }

// === Upload CSV using Bearer Token ===
let UploadToLoadViewer (csvData: string) (jwtToken: string) =
    task {
        let mutable retry = 3
        while retry > 0 do
            try
                use client = new HttpClient()
                client.DefaultRequestHeaders.Authorization <- System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", jwtToken)
                let content = new StringContent(csvData, System.Text.Encoding.UTF8, "text/csv")
                let! response = client.PostAsync(LoadViewerUploadURL, content)
                if response.StatusCode = System.Net.HttpStatusCode.OK || response.StatusCode = System.Net.HttpStatusCode.Accepted then
                    return sprintf "%i - %s" (int response.StatusCode) (response.Content.ReadAsStringAsync().Result)
                elif response.StatusCode = System.Net.HttpStatusCode.Gone then
                    let! newToken = getJwtToken ()
                    if newToken.StartsWith("ERROR") || newToken.StartsWith("Exception") then
                        return newToken
                    jwtToken <- newToken
                    retry <- retry - 1
                else
                    return sprintf "ERROR: %i - %s" (int response.StatusCode) (response.Content.ReadAsStringAsync().Result)
            with ex ->
                return sprintf "Exception: %s" ex.Message
        return "Upload failed after retries."
    }

// === Cleans text for CSV: removes commas and replaces line breaks ===
// === Use this for all string fields before writing to CSV ===
//
// Replaces:
//     ,     ?     (removed)
//     \r\n, \r, \n ? " " (space)
let removeComma (value: string) =
    if String.IsNullOrEmpty(value) then ""
    else
        value.Trim().Replace(",", "").Replace("\r\n", " ").Replace("\r", " ").Replace("\n", " ")

// === Converts numeric value to string with up to 3 decimal places ===
// === Trailing zeros are removed. Use only for Weight-related fields ===
//
// Examples:
//     Convert 91.5      ? "91.5"
//     Convert 91.56789  ? "91.568"
//     Keep 52           ? "52"    (no .000 added)
let formatDecimal3 (value: float) =
    sprintf "%.3f" value |> float |> string

// === OPTIONAL TEST CALL ===
// Make calls Examples
//     testUpload () //-> CSVFromHardCoded
//===============================================================
let testUpload () =
    task {
        let csv = csvCreate ()
        let! token = if String.IsNullOrEmpty LoadViewerToken then getJwtToken () else async.Return LoadViewerToken |> Async.AwaitTask
        if token.StartsWith("ERROR") || token.StartsWith("Exception") then
            printfn "Token Error or Server Not Reachable: \n%s" token
        else
            let! response = UploadToLoadViewer csv token
            printfn "%s" response
    }
Go

package main

import (
        "bytes"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "net/http"
        "strconv"
        "strings"
)

// ========================================
// Module: modLoadViewerCSV
// Purpose:
//      1. Generate LoadViewer-compatible CSV from hardcoded or database values
//      2. Upload CSV to LoadViewer using HTTP POST (http.Client)
// Includes helper functions:
//      - removeComma: for all string fields
//      - formatDecimal3: for weights with decimals
// ========================================

// === Constants ===
const (
        LoadViewerTokenURL  = "https://www.loadviewer.com/api/auth/jwt/token"
        LoadViewerUploadURL = "https://www.loadviewer.com/api/integration/v1/upload"
        LoadViewerAPIEmail  = "YOUR_Email"
        LoadViewerAPIKey    = "YOUR_API_KEY_HERE"
)

// Optional: You can fetch the above values from file or DB:
//      - From config files, or from a database config table
//      - From encrypted DB table
//      - From environment variables (os.Getenv in Go)

// === Module level variable to cache the retrieved token ===
var LoadViewerToken string

// === Dynamically generates LoadViewer CSV from hardcoded data ===
func csvGenerate() string {
        var csv strings.Builder
        shipmentData := map[string]interface{}{
                "Reference":             "SANDBOX-01",
                "HangingAllowed":        true,
                "UseMultipleContainers": false,
        }

        containerData := map[string]interface{}{
                "Length":    5867,
                "Width":     2352,
                "Height":    2393,
                "MaxWeight": 27200.0,
                "BackMargin": 0,
        }

        cargoData := map[string]interface{}{
                "Color":                 "#008000",
                "Length":                700,
                "Width":                 500,
                "Height":                300,
                "Weight":                91.5,
                "Cartons":               300,
                "AdditionalCartons":     0,
                "Placement":             0,
                "VerticalRotationAllowed": true,
                "SKUPerCarton":          1,
                "EmptyPalletHeight":     0,
                "TrayCap":               0,
                "TrayHeight":            0,
                "AirSpaceLength":        0,
                "AirSpaceWidth":         0,
                "AirSpaceHeight":        0,
                "NameOfSet":             "",
                "CartonRatio":           0,
                "Description":           "",
                "Destination":           0,
                "PO":                    "",
                "Shipper":               "",
        }

        // --- Begin CSV output ---
        csv.WriteString("S,H,Reference,Hanging Allowed,Use Multiple Containers\n")
        csv.WriteString(fmt.Sprintf("S,D,%s,%t,%t\n", removeComma(shipmentData["Reference"].(string)), shipmentData["HangingAllowed"].(bool), shipmentData["UseMultipleContainers"].(bool)))
        csv.WriteString("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n")
        csv.WriteString(fmt.Sprintf("B,D,%d,%d,%d,%s,%d\n", containerData["Length"].(int), containerData["Width"].(int), containerData["Height"].(int), formatDecimal3(containerData["MaxWeight"].(float64)), containerData["BackMargin"].(int)))
        csv.WriteString("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n")
        csv.WriteString(fmt.Sprintf("C,D,%s,%d,%d,%d,%s,%d,%d,%d,%t,%d,%d,%d,%d,%d,%d,%d,%s,%d,%s,%d,%s,%s\n",
                removeComma(cargoData["Color"].(string)), cargoData["Length"].(int), cargoData["Width"].(int), cargoData["Height"].(int),
                formatDecimal3(cargoData["Weight"].(float64)), cargoData["Cartons"].(int), cargoData["AdditionalCartons"].(int), cargoData["Placement"].(int),
                cargoData["VerticalRotationAllowed"].(bool), cargoData["SKUPerCarton"].(int), cargoData["EmptyPalletHeight"].(int), cargoData["TrayCap"].(int),
                cargoData["TrayHeight"].(int), cargoData["AirSpaceLength"].(int), cargoData["AirSpaceWidth"].(int), cargoData["AirSpaceHeight"].(int),
                removeComma(cargoData["NameOfSet"].(string)), cargoData["CartonRatio"].(int), removeComma(cargoData["Description"].(string)),
                cargoData["Destination"].(int), removeComma(cargoData["PO"].(string)), removeComma(cargoData["Shipper"].(string))))
        return csv.String()
}

// === Generate CSV string for SANDBOX-01 ===
func csvCreate() string {
        var csv strings.Builder
        csv.WriteString("S,H,Reference,Hanging Allowed,Use Multiple Containers\n")
        csv.WriteString("S,D,SANDBOX-01,True,False\n")
        csv.WriteString("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n")
        csv.WriteString("B,D,5867,2352,2393,27200,0\n")
        csv.WriteString("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n")
        csv.WriteString("C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,\n")
        return csv.String()
}

// === Get JWT Token ===
func getJwtToken() (string, error) {
        payload := map[string]string{
                "email":  LoadViewerAPIEmail,
                "apikey": LoadViewerAPIKey,
        }
        jsonPayload, err := json.Marshal(payload)
        if err != nil {
                return "", err
        }

        resp, err := http.Post(LoadViewerTokenURL, "application/json", bytes.NewBuffer(jsonPayload))
        if err != nil {
                return "", err
        }
        defer resp.Body.Close()

        body, err := io.ReadAll(resp.Body)
        if err != nil {
                return "", err
        }

        if resp.StatusCode == http.StatusOK {
                var response map[string]string
                if err := json.Unmarshal(body, &response); err != nil {
                        return "", err
                }
                LoadViewerToken = response["token"]
                return response["token"], nil
        }
        return fmt.Sprintf("ERROR: %d - %s", resp.StatusCode, string(body)), nil
}

// === Upload CSV using Bearer Token ===
func UploadToLoadViewer(csvData, jwtToken string) (string, error) {
        retry := 3
        for retry > 0 {
                req, err := http.NewRequest("POST", LoadViewerUploadURL, bytes.NewBuffer([]byte(csvData)))
                if err != nil {
                        return "", err
                }
                req.Header.Set("Content-Type", "text/csv")
                req.Header.Set("Authorization", "Bearer "+jwtToken)

                client := &http.Client{}
                resp, err := client.Do(req)
                if err != nil {
                        return "", err
                }
                defer resp.Body.Close()

                body, err := io.ReadAll(resp.Body)
                if err != nil {
                        return "", err
                }

                if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusAccepted {
                        return fmt.Sprintf("%d - %s", resp.StatusCode, string(body)), nil
                } else if resp.StatusCode == http.StatusGone {
                        token, err := getJwtToken()
                        if err != nil {
                                return "", err
                        }
                        if strings.HasPrefix(token, "ERROR") {
                                return token, nil
                        }
                        jwtToken = token
                        retry--
                } else {
                        return fmt.Sprintf("ERROR: %d - %s", resp.StatusCode, string(body)), nil
                }
        }
        return "Upload failed after retries.", nil
}

// === Cleans text for CSV: removes commas and replaces line breaks ===
// === Use this for all string fields before writing to CSV ===
//
// Replaces:
//      ,       ?       (removed)
//      \r\n, \r, \n   ? " " (space)
func removeComma(value string) string {
        s := strings.TrimSpace(value)
        s = strings.ReplaceAll(s, ",", "")
        s = strings.ReplaceAll(s, "\r\n", " ")
        s = strings.ReplaceAll(s, "\r", " ")
        s = strings.ReplaceAll(s, "\n", " ")
        return s
}

// === Converts numeric value to string with up to 3 decimal places ===
// === Trailing zeros are removed. Use only for Weight-related fields ===
//
// Examples:
//      Convert 91.5        ? "91.5"
//      Convert 91.56789    ? "91.568"
//      Keep 52             ? "52"    (no .000 added)
func formatDecimal3(value float64) string {
        return strconv.FormatFloat(value, 'f', 3, 64)
}

// === OPTIONAL TEST CALL ===
// Make calls Examples
//      testUpload() //-> CSVFromHardCoded
// ===============================================================
func testUpload() {
        csv := csvCreate()
        token := LoadViewerToken
        if token == "" {
                tok, err := getJwtToken()
                if err != nil {
                        log.Println(err)
                        return
                }
                token = tok
        }
        if strings.HasPrefix(token, "ERROR") {
                log.Println("Token Error or Server Not Reachable: \n" + token)
                return
        }
        resp, err := UploadToLoadViewer(csv, token)
        if err != nil {
                log.Println(err)
                return
        }
        log.Println(resp)
}

func main() {
        // testUpload()
}
R

# ========================================
# Module: modLoadViewerCSV
# Purpose:
#     1. Generate LoadViewer-compatible CSV from hardcoded or database values
#     2. Upload CSV to LoadViewer using HTTP POST (httr package)
# Includes helper functions:
#     - removeComma: for all string fields
#     - formatDecimal3: for weights with decimals
# ========================================

# === Constants ===
LoadViewerTokenURL <- "https://www.loadviewer.com/api/auth/jwt/token"
                      LoadViewerUploadURL <- "https://www.loadviewer.com/api/integration/v1/upload"
LoadViewerAPIEmail <- "YOUR_Email"
LoadViewerAPIKey <- "YOUR_API_KEY_HERE"

# Optional: You can fetch the above values from file or DB:
#     - From config files, or from a database config table
#     - From encrypted DB table
#     - From environment variables (Sys.getenv in R)

# === Module level variable to cache the retrieved token ===
LoadViewerToken <- ""

# === Dynamically generates LoadViewer CSV from hardcoded data ===
csvGenerate <- function() {
  shipmentData <- list(
    Reference = "SANDBOX-01",
    HangingAllowed = TRUE,
    UseMultipleContainers = FALSE
  )

  containerData <- list(
    Length = 5867,
    Width = 2352,
    Height = 2393,
    MaxWeight = 27200,
    BackMargin = 0
  )

  cargoData <- list(
    Color = "#008000",
    Length = 700,
    Width = 500,
    Height = 300,
    Weight = 91.5,
    Cartons = 300,
    AdditionalCartons = 0,
    Placement = 0,
    VerticalRotationAllowed = TRUE,
    SKUPerCarton = 1,
    EmptyPalletHeight = 0,
    TrayCap = 0,
    TrayHeight = 0,
    AirSpaceLength = 0,
    AirSpaceWidth = 0,
    AirSpaceHeight = 0,
    NameOfSet = "",
    CartonRatio = 0,
    Description = "",
    Destination = 0,
    PO = "",
    Shipper = ""
  )

  # --- Begin CSV output ---
  csv <- "S,H,Reference,Hanging Allowed,Use Multiple Containers\n"
  csv <- paste0(csv, "S,D,", removeComma(shipmentData$Reference), ",", shipmentData$HangingAllowed, ",", shipmentData$UseMultipleContainers, "\n")
  csv <- paste0(csv, "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n")
  csv <- paste0(csv, "B,D,", containerData$Length, ",", containerData$Width, ",", containerData$Height, ",", formatDecimal3(containerData$MaxWeight), ",", containerData$BackMargin, "\n")
  csv <- paste0(csv, "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n")
  csv <- paste0(csv, "C,D,", removeComma(cargoData$Color), ",", cargoData$Length, ",", cargoData$Width, ",", cargoData$Height, ",", formatDecimal3(cargoData$Weight), ",", cargoData$Cartons, ",", cargoData$AdditionalCartons, ",", cargoData$Placement, ",", cargoData$VerticalRotationAllowed, ",", cargoData$SKUPerCarton, ",", cargoData$EmptyPalletHeight, ",", cargoData$TrayCap, ",", cargoData$TrayHeight, ",", cargoData$AirSpaceLength, ",", cargoData$AirSpaceWidth, ",", cargoData$AirSpaceHeight, ",", removeComma(cargoData$NameOfSet), ",", cargoData$CartonRatio, ",", removeComma(cargoData$Description), ",", cargoData$Destination, ",", removeComma(cargoData$PO), ",", removeComma(cargoData$Shipper), "\n")

  return(csv)
}

# === Generate CSV string for SANDBOX-01 ===
csvCreate <- function() {
  csv <- "S,H,Reference,Hanging Allowed,Use Multiple Containers\n"
  csv <- paste0(csv, "S,D,SANDBOX-01,TRUE,FALSE\n")
  csv <- paste0(csv, "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n")
  csv <- paste0(csv, "B,D,5867,2352,2393,27200,0\n")
  csv <- paste0(csv, "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n")
  csv <- paste0(csv, "C,D,#008000,700,500,300,91.5,300,0,0,TRUE,1,0,0,0,0,0,0,,0,,0,,\n")
  return(csv)
}

# === Get JWT Token ===
getJwtToken <- function() {
  tryCatch({
    response <- httr::POST(
      LoadViewerTokenURL,
      httr::content_type_json(),
      body = list(email = LoadViewerAPIEmail, apikey = LoadViewerAPIKey)
    )

    if (httr::status_code(response) == 200) {
      json <- httr::content(response, "parsed")
      token <- json$token
      LoadViewerToken <<- token # Cache the token
      return(token)
    } else {
      return(paste0("ERROR: ", httr::status_code(response), " - ", httr::content(response, "text")))
    }
  }, error = function(e) {
    return(paste0("Exception: ", e$message))
  })
}

# === Upload CSV using Bearer Token ===
UploadToLoadViewer <- function(csvData, jwtToken) {
  retry <- 3
  while (retry > 0) {
    tryCatch({
      response <- httr::POST(
        LoadViewerUploadURL,
        httr::content_type("text/csv"),
        httr::add_headers(Authorization = paste0("Bearer ", jwtToken)),
        body = csvData
      )

      if (httr::status_code(response) %in% c(200, 202)) {
        return(paste0(httr::status_code(response), " - ", httr::content(response, "text")))
      } else if (httr::status_code(response) == 410) {
        jwtToken <- getJwtToken()
        if (startsWith(jwtToken, "ERROR") || startsWith(jwtToken, "Exception")) {
          return(jwtToken)
        }
        retry <- retry - 1
      } else {
        return(paste0("ERROR: ", httr::status_code(response), " - ", httr::content(response, "text")))
      }
    }, error = function(e) {
      return(paste0("Exception: ", e$message))
    })
  }
  return("Upload failed after retries.")
}

# === Cleans text for CSV: removes commas and replaces line breaks ===
# === Use this for all string fields before writing to CSV ===
#
# Replaces:
#     ,     ?     (removed)
#     \r\n, \r, \n ? " " (space)
removeComma <- function(value) {
  if (is.null(value)) {
    return("")
  }
  s <- trimws(value)
  s <- gsub(",", "", s)
  s <- gsub("[\r\n]", " ", s)
  return(s)
}

# === Converts numeric value to string with up to 3 decimal places ===
# === Trailing zeros are removed. Use only for Weight-related fields ===
#
# Examples:
#     Convert 91.5      ? "91.5"
#     Convert 91.56789  ? "91.568"
#     Keep 52           ? "52"    (no .000 added)
formatDecimal3 <- function(value) {
  if (is.null(value) || !is.numeric(value)) {
    return("0")
  }
  return(format(round(value, 3), nsmall = 3, scientific = FALSE))
}

# === OPTIONAL TEST CALL ===
# Make calls Examples
#     testUpload() //-> CSVFromHardCoded
#===============================================================
testUpload <- function() {
  csv <- csvCreate()
  token <- LoadViewerToken
  if (token == "") {
    token <- getJwtToken()
  }
  if (startsWith(token, "ERROR") || startsWith(token, "Exception")) {
    print(paste0("Token Error or Server Not Reachable: \n", token))
    return()
  }
  response <- UploadToLoadViewer(csv, token)
  print(response)
}
PHP

<?php
// ========================================
// Module: modLoadViewerCSV
// Purpose:
//     1. Generate LoadViewer-compatible CSV from hardcoded or database values
//     2. Upload CSV to LoadViewer using HTTP POST (cURL)
// Includes helper functions:
//     - removeComma: for all string fields
//     - formatDecimal3: for weights with decimals
// ========================================

// === Constants ===
const LoadViewerTokenURL = 'https://www.loadviewer.com/api/auth/jwt/token';
const LoadViewerUploadURL = 'https://www.loadviewer.com/api/integration/v1/upload';
const LoadViewerAPIEmail = 'YOUR_Email';
const LoadViewerAPIKey = 'YOUR_API_KEY_HERE';

// Optional: You can fetch the above values from file or DB:
//     - From config files, or from a database config table
//     - From encrypted DB table
//     - From environment variables (getenv in PHP)

// === Module level variable to cache the retrieved token ===
$LoadViewerToken = '';

// === Dynamically generates LoadViewer CSV from hardcoded data ===
function csvGenerate() {
    $shipmentData = [
        'Reference' => 'SANDBOX-01',
        'HangingAllowed' => true,
        'UseMultipleContainers' => false,
    ];

    $containerData = [
        'Length' => 5867,
        'Width' => 2352,
        'Height' => 2393,
        'MaxWeight' => 27200,
        'BackMargin' => 0,
    ];

    $cargoData = [
        'Color' => '#008000',
        'Length' => 700,
        'Width' => 500,
        'Height' => 300,
        'Weight' => 91.5,
        'Cartons' => 300,
        'AdditionalCartons' => 0,
        'Placement' => 0,
        'VerticalRotationAllowed' => true,
        'SKUPerCarton' => 1,
        'EmptyPalletHeight' => 0,
        'TrayCap' => 0,
        'TrayHeight' => 0,
        'AirSpaceLength' => 0,
        'AirSpaceWidth' => 0,
        'AirSpaceHeight' => 0,
        'NameOfSet' => '',
        'CartonRatio' => 0,
        'Description' => '',
        'Destination' => 0,
        'PO' => '',
        'Shipper' => '',
    ];

    // --- Begin CSV output ---
    $csv = 'S,H,Reference,Hanging Allowed,Use Multiple Containers\n';
    $csv .= 'S,D,' . removeComma($shipmentData['Reference']) . ',' . ($shipmentData['HangingAllowed'] ? 'TRUE' : 'FALSE') . ',' . ($shipmentData['UseMultipleContainers'] ? 'FALSE' : 'TRUE') . '\n';
    $csv .= 'B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n';
    $csv .= 'B,D,' . $containerData['Length'] . ',' . $containerData['Width'] . ',' . $containerData['Height'] . ',' . formatDecimal3($containerData['MaxWeight']) . ',' . $containerData['BackMargin'] . '\n';
    $csv .= 'C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n';
    $csv .= 'C,D,' . removeComma($cargoData['Color']) . ',' . $cargoData['Length'] . ',' . $cargoData['Width'] . ',' . $cargoData['Height'] . ',' . formatDecimal3($cargoData['Weight']) . ',' . $cargoData['Cartons'] . ',' . $cargoData['AdditionalCartons'] . ',' . $cargoData['Placement'] . ',' . ($cargoData['VerticalRotationAllowed'] ? 'TRUE' : 'FALSE') . ',' . $cargoData['SKUPerCarton'] . ',' . $cargoData['EmptyPalletHeight'] . ',' . $cargoData['TrayCap'] . ',' . $cargoData['TrayHeight'] . ',' . $cargoData['AirSpaceLength'] . ',' . $cargoData['AirSpaceWidth'] . ',' . $cargoData['AirSpaceHeight'] . ',' . removeComma($cargoData['NameOfSet']) . ',' . $cargoData['CartonRatio'] . ',' . removeComma($cargoData['Description']) . ',' . $cargoData['Destination'] . ',' . removeComma($cargoData['PO']) . ',' . removeComma($cargoData['Shipper']) . '\n';

    return $csv;
}

// === Generate CSV string for SANDBOX-01 ===
function csvCreate() {
    $csv = 'S,H,Reference,Hanging Allowed,Use Multiple Containers\n';
    $csv .= 'S,D,SANDBOX-01,TRUE,FALSE\n';
    $csv .= 'B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n';
    $csv .= 'B,D,5867,2352,2393,27200,0\n';
    $csv .= 'C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n';
    $csv .= 'C,D,#008000,700,500,300,91.5,300,0,0,TRUE,1,0,0,0,0,0,0,,0,,0,,\n';
    return $csv;
}

// === Get JWT Token ===
function getJwtToken() {
    global $LoadViewerToken;
    $postData = json_encode(['email' => LoadViewerAPIEmail, 'apikey' => LoadViewerAPIKey]);

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, LoadViewerTokenURL);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    curl_setopt($ch, CURLOPT_TIMEOUT, 5);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode == 200) {
        $json = json_decode($response, true);
        $LoadViewerToken = $json['token'];
        return $json['token'];
    } else {
        return 'ERROR: ' . $httpCode . ' - ' . $response;
    }
}

// === Upload CSV using Bearer Token ===
function UploadToLoadViewer($csvData, $jwtToken) {
    $retry = 3;
    while ($retry > 0) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, LoadViewerUploadURL);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $csvData);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: text/csv', 'Authorization: Bearer ' . $jwtToken]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode == 200 || $httpCode == 202) {
            return $httpCode . ' - ' . $response;
        } else if ($httpCode == 410) {
            $jwtToken = getJwtToken();
            if (strpos($jwtToken, 'ERROR') === 0) {
                return $jwtToken;
            }
            $retry--;
        } else {
            return 'ERROR: ' . $httpCode . ' - ' . $response;
        }
    }
    return 'Upload failed after retries.';
}

// === Cleans text for CSV: removes commas and replaces line breaks ===
// === Use this for all string fields before writing to CSV ===
//
// Replaces:
//     ,     ?     (removed)
//     \r\n, \r, \n ? ' ' (space)
function removeComma($value) {
    if ($value === null) {
        return '';
    }
    $s = trim($value);
    $s = str_replace(',', '', $s);
    $s = preg_replace('/[\r\n]+/', ' ', $s);
    return $s;
}

// === Converts numeric value to string with up to 3 decimal places ===
// === Trailing zeros are removed. Use only for Weight-related fields ===
//
// Examples:
//     Convert 91.5      ? '91.5'
//     Convert 91.56789  ? '91.568'
//     Keep 52           ? '52'    (no .000 added)
function formatDecimal3($value) {
    if ($value === null || !is_numeric($value)) {
        return '0';
    }
    return rtrim(sprintf('%.3f', $value), '0.');
}

// === OPTIONAL TEST CALL ===
// Make calls Examples
//     testUpload() //-> CSVFromHardCoded
//===============================================================
function testUpload() {
    global $LoadViewerToken;
    $csv = csvCreate();
    $token = $LoadViewerToken;
    if (empty($token)) {
        $token = getJwtToken();
    }
    if (strpos($token, 'ERROR') === 0) {
        echo 'Token Error or Server Not Reachable: \n' . $token;
        return;
    }
    $response = UploadToLoadViewer($csv, $token);
    echo $response;
}
?>
Dart

import 'dart:convert';
import 'package:http/http.dart' as http;

// ========================================
// Module: modLoadViewerCSV
// Purpose:
//     1. Generate LoadViewer-compatible CSV from hardcoded or database values
//     2. Upload CSV to LoadViewer using HTTP POST (http package)
// Includes helper functions:
//     - removeComma: for all string fields
//     - formatDecimal3: for weights with decimals
// ========================================

// === Constants ===
const String LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token";
const String LoadViewerUploadURL = "https://www.loadviewer.com/api/integration/v1/upload";
const String LoadViewerAPIEmail = "YOUR_Email";
const String LoadViewerAPIKey = "YOUR_API_KEY_HERE";

// Optional: You can fetch the above values from file or DB:
//     - From config files, or from a database config table
//     - From encrypted DB table
//     - From environment variables (Platform.environment in Dart)

// === Module level variable to cache the retrieved token ===
String LoadViewerToken = "";

// === Dynamically generates LoadViewer CSV from hardcoded data ===
String csvGenerate() {
  Map<String, dynamic> shipmentData={
    "Reference": "SANDBOX-01",
    "HangingAllowed": true,
    "UseMultipleContainers": false,
  };
              Map<String, dynamic> containerData = {
    "Length": 5867,
    "Width": 2352,
    "Height": 2393,
    "MaxWeight": 27200.0,
    "BackMargin": 0,
  };

  Map<String, dynamic> cargoData = {
    "Color": "#008000",
    "Length": 700,
    "Width": 500,
    "Height": 300,
    "Weight": 91.5,
    "Cartons": 300,
    "AdditionalCartons": 0,
    "Placement": 0,
    "VerticalRotationAllowed": true,
    "SKUPerCarton": 1,
    "EmptyPalletHeight": 0,
    "TrayCap": 0,
    "TrayHeight": 0,
    "AirSpaceLength": 0,
    "AirSpaceWidth": 0,
    "AirSpaceHeight": 0,
    "NameOfSet": "",
    "CartonRatio": 0,
    "Description": "",
    "Destination": 0,
    "PO": "",
    "Shipper": "",
  };

  // --- Begin CSV output ---
  String csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers\n";
  csv += "S,D,<span class="math-inline">\{removeComma\(shipmentData\["Reference"\]\)\},</span>{shipmentData["HangingAllowed"]},<span class="math-inline" >\{shipmentData\["UseMultipleContainers"\]\}\\n";
csv \+\= "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\\n";
csv \+\= "B,D,</span>{containerData["Length"]},<span class="math-inline" >\{containerData\["Width"\]\},</span>{containerData["Height"]},<span class="math-inline" >\{formatDecimal3\(containerData\["MaxWeight"\]\)\},</span>{containerData["BackMargin"]}\n";
  csv += "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n";
  csv += "C,D,<span class="math-inline">\{removeComma\(cargoData\["Color"\]\)\},</`>{cargoData["Length"]},<span class="math-inline" >\{cargoData\["Width"\]\},</span>{cargoData["Height"]},<span class="math-inline" >\{formatDecimal3\(cargoData\["Weight"\]\)\},</span>{cargoData["Cartons"]},<span class="math-inline" >\{cargoData\["AdditionalCartons"\]\},</span>{cargoData["Placement"]},<span class="math-inline" >\{cargoData\["VerticalRotationAllowed"\]\},</span>{cargoData["SKUPerCarton"]},<span class="math-inline" >\{cargoData\["EmptyPalletHeight"\]\},</span>{cargoData["TrayCap"]},<span class="math-inline" >\{cargoData\["TrayHeight"\]\},</span>{cargoData["AirSpaceLength"]},<span class="math-inline" >\{cargoData\["AirSpaceWidth"\]\},</span>{cargoData["AirSpaceHeight"]},<span class="math-inline" >\{removeComma\(cargoData\["NameOfSet"\]\)\},</span>{cargoData["CartonRatio"]},<span class="math-inline" >\{removeComma\(cargoData\["Description"\]\)\},</span>{cargoData["Destination"]},<span class="math-inline" >\{removeComma\(cargoData\["PO"\]\)\},</span>{removeComma(cargoData["Shipper"])}\n";

  return csv;
}

// === Generate CSV string for SANDBOX-01 ===
String csvCreate() {
  String csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers\n";
  csv += "S,D,SANDBOX-01,TRUE,FALSE\n";
  csv += "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n";
  csv += "B,D,5867,2352,2393,27200,0\n";
  csv += "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n";
  csv += "C,D,#008000,700,500,300,91.5,300,0,0,TRUE,1,0,0,0,0,0,0,,0,,0,,\n";
  return csv;
}

// === Get JWT Token ===
Future<String> getJwtToken() async {
  try {
    final response = await http.post(
      Uri.parse(LoadViewerTokenURL),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({"email": LoadViewerAPIEmail, "apikey": LoadViewerAPIKey}),
    );

    if (response.statusCode == 200) {
      final json = jsonDecode(response.body);
      LoadViewerToken = json['token'];
      return json['token'];
    } else {
      return "ERROR: ${response.statusCode} - ${response.body}";
    }
  } catch (e) {
    return "Exception: $e";
  }
}

// === Upload CSV using Bearer Token ===
Future<String> UploadToLoadViewer(String csvData, String jwtToken) async {
  int retry = 3;
  while (retry > 0) {
    try {
      final response = await http.post(
        Uri.parse(LoadViewerUploadURL),
        headers: {'Content-Type': 'text/csv', 'Authorization': 'Bearer <span class="math-inline">jwtToken'\},
body\: csvData,
\);
if \(response\.statusCode \=\= 200 \|\| response\.statusCode \=\= 202\) \{
return "</span>{response.statusCode} - ${response.body}";
      } else if (response.statusCode == 410) {
        jwtToken = await getJwtToken();
        if (jwtToken.startsWith("ERROR")) {
          return jwtToken;
        }
        retry--;
      } else {
        return "ERROR: ${response.statusCode} - ${response.body}";
      }
    } catch (e) {
      return "Exception: <span class="math-inline">e";
\}
\}
return "Upload failed after retries\.";
\}
// \=\=\= Cleans text for CSV\: removes commas and replaces line breaks \=\=\=
// \=\=\= Use this for all string fields before writing to CSV \=\=\=
//
// Replaces\:
//     ,     ?     \(removed\)
//     \\r\\n, \\r, \\n ? " " \(space\)
String removeComma\(String value\) \{
if \(value \=\= null\) \{
return "";
\}
String s \= value\.trim\(\);
s \= s\.replaceAll\(",", ""\);
s \= s\.replaceAll\(RegExp\(r"\[\\r\\n\]\+"\), " "\);
return s;
\}
// \=\=\= Converts numeric value to string with up to 3 decimal places \=\=\=
// \=\=\= Trailing zeros are removed\. Use only for Weight\-related fields \=\=\=
//
// Examples\:
//     Convert 91\.5      ? "91\.5"
//     Convert 91\.56789  ? "91\.568"
//     Keep 52           ? "52"    \(no \.000 added\)
String formatDecimal3\(double value\) \{
if \(value \=\= null \|\| value\.isNaN\) \{
return "0";
\}
return value\.toStringAsFixed\(3\)\.replaceAll\(RegExp\(r"0\*</span>"), '').replaceAll(RegExp(r"\.$"), '');
}

// === OPTIONAL TEST CALL ===
// Make calls Examples
//     testUpload() //-> CSVFromHardCoded
//===============================================================
Future<void> testUpload() async {
  String csv = csvCreate();
  String token = LoadViewerToken;
  if (token.isEmpty) {
    token = await getJwtToken();
  }
  if (token.startsWith("ERROR")) {
    print("Token Error or Server Not Reachable: \n$token");
    return;
  }
  String response = await UploadToLoadViewer(csv, token);
  print(response);
}

Important Reminders
  • Requests without API Key will return HTTP 400 or 401
  • Requests with expired taken shall return HTTP 410 you should replace your cached token with new one from LoadViewer.
  • Use SANDBOX-* Shipment References to avoid real charges, these shipments are not published.
  • Accepted Shipment always retrun Success with code 202 with Draft status. SANDBOX shipments and other valid shipment that cannot be published are Saved with Draft Status.
  • Accepted and Published Shipment always retrun Success with code 200 with Coins Charges and Coin Balance after charge.
  • Use the manual file upload method as your first validation step
  • Use the Shipments page to delete the SHIPMENTS to try again.
  • Always verify success and no error structure in responses