Billig Shipping Script
The Billig shipping script, named 'Export_Billig' in the Airtox File, is for sending shipping orders to Billig (BA) for processing.
It runs as a MW script and uses CURL to send shipping orders over tcp/http. It is triggered by selecting Sales Invoices and pressing the “Export Billig” button.
Technical documentation
Key features:
MW Sales Invoice Sending: Sends selected sales invoices to the Billig
API.
Checks Sales Invoices not already processed: Uses Transaction field user_4. Puts a timestamp if already sent. err:err text if error.
Validates Delivery Addresses: Checks the delivery address meets criteria. More info below
Validates Country 2 Char representation: Billig
API uses 2 Char country digits for addresses. Uses JSON file to verify.
Missing features:
Unable to resend if send failure. (MW script is probably not the place to do this. It is busy enough as it is)
No local record of sent orders (with MW SQLite, this could be done. Again, script is pretty busy already)
If when adding a Sales Line to Sales Header fails…. no resend available as per above. Just gets left off order (has only happened a few times)
UI feedback while working. Bit of a limitation of long running MW script. (Only recommend sending max of 10 Sales Invoices)
API is locked down by IPs. The available IPs are Martin's Office IP, Airtox's Server IP, and possibly TMF and/or Sussol IPs.
API looks like it is maintained by prentow.com (Contacts not added here as public page)
Test Site Enpoints
Live Site Enpoints
Auth info is hard coded in script. Test Auth info is commented out.
The API is what can be deduced as a Microsoft Dynamics 365 API. The API is very basic and not very ideal, but it has been rock solid for 3 years despite its shortcomings.
Some of those short commings are:
Cannot immediately check for duplicated orders. This is because orders are not available for retrieval until Billig manually processes them!

Uses HTTP and not HTTPS
The test site is never updated with data and rules from the “live” site, which makes it impossible to test anything properly. If any new shipping codes are added, just have to add code to live script, and hope you do it correctly.
Uses antiquated NTLM for authentication. Luckily CURL has a method for this with option. Thanks Craig for finding this
Curl_SetOpt(ch, CURLOPT_HTTPAUTH, 8)
How API Works
Before adding a Sales Header, check if order already exists using the Header Check duplication endpoint
A Sales Header is created using the Sales Header endpoint
Individual lines are added to Sales Header using the Lines Header endpoint
Document_No = “AI” + transaction.OurRef
External_Docuement_No = “Inv-” + Transaction.OurRef + “/Ord-” + Transaction.TheirRef
shippingCodeLines = See below how shippingCodeLines are calculated
let pdata = "{\n "
let pdata = pdata + "\"Document_Type\": \"Ordre\",\n "
let pdata = pdata + "\"Document_No\": \"" + docNumber + "\",\n "
let pdata = pdata + "\"Customer_No\": \"" + billigCompanyNoPROP + "\",\n "
let pdata = pdata + "\"Shipment_Date\": \"" + shipDate + "\",\n "
let pdata = pdata + "\"Ship_to_Name\": \"" + shipToName + "\",\n "
let pdata = pdata + "\"Ship_to_Address\": \"" + addr1 + "\",\n "
let pdata = pdata + "\"Ship_to_Address_2\" : \"" + addr2 + "\",\n "
let pdata = pdata + "\"Ship_to_City\": \"" + shipToCity + "\",\n "
let pdata = pdata + "\"Ship_to_Post_Code\": \"" + shipToPostcode + "\",\n "
let pdata = pdata + "\"Ship_to_Country_Region_Code\": \"" + shipToCountry + "\",\n "
let pdata = pdata + shippingCodeLines
let pdata = pdata + "\"External_Document_No\": \""+ extDocNumber +"\""
let pdata = pdata + "\n}
Sales Line Fields (text from script)
Document_No = “AI” + transaction.OurRef
Line_No = sequential integer number. 1, 2, 3, etc…
let pdata = "{\n "
let pdata = pdata + "\"Document_Type\": \"Ordre\",\n "
let pdata = pdata + "\"Document_No\": \"" + docNumber + "\",\n"
let pdata = pdata + "\"Line_No\": \"" + lineNum + "\",\n"
let pdata = pdata + "\"Type\": \"Vare\",\n"
let pdata = pdata + "\"Item_No\": \"" + itemCode + "\",\n"
let pdata = pdata + "\"Quantity\" : " + quantity
let pdata = pdata + "\n}"
Shipping Code Lines
A few shipping codes are sent in the Sales Header. It consists of 2 fields.
The logic depends (as of today, 17th Feb 2026):
If internal order is for customer “BILLIG”. These are interal orders sent to Billig warehouse.
If destination country “DK” (Denmark)
If destination country “FO”, or “GL”, or “IS”
If destination country is none of the above, and not “DK”
Entry point:
on Before:F_TRANSLIST(windowRef)
InstallToolBarIcon(windowRef, "Export_Billig")
end
Script Logic and Steps
User highlights Sales Invoices to send and presses “Export Billig” button (recommended to only select a max of 10 as unable to give UI feedback of running process)
Check each Transaction is of type “DI@” (debit invoice/Sales Invoice). Reject operation if not Sales Invoice
Check Transaction.user_4 field is empty AND != “Err:NoResponse”. Reject if has timestamp or if equal to anything but “Err:NoResponse”
Check there are Transactions selected. Reject if none selected
Attempt to send transactions with following validations for each transaction
Validate again if user_4 is empty or anything but “Err:NoResponse” (redundant really as already did this!)
Check if Web Order. Check transaction.flag = “WEB”. This feature is not being used as Airtox not doing web sales
Parse transaction delivery addresses and validate
Address must be 4, 5, or 6 lines
If only 4 lines, third line must have City and Postcode on same line, in that order
First line is always the Recipient Name
Last line is always the country name, or country 2 code letters, nothing more. i.e “Denmark” or “DK”, “Denmark 8444” would fail.
Second to last line must always be JUST postocde, or have postcode as last value on line. i.e “Coppenhagen 8000”
Parse for 2 Char country code if full country name is given
Check all Transaction lines are from the “BA” location
Check order doesn't already exist with Billig
Create Sales Header
Add lines to Sales Header, one by one