Welcome to our DOCS site
Here at BEX we frequently develop and publish new API’s to assist our customers who wish to gain deeper control and visibility into their courier processes.
These API’s allow for direct integration into our courier platform, allowing data to be read and created using requests in JSON format. Couple this with our token based Authentication methods and standard HTTP verbs (which are understood by most HTTP clients) you have the capability to orchestrate and manage a number of processes within the courier environment.
We hope that you will build something great and benefit from integrating with our courier API’s.
We want your courier journey to be pain-free - If you run into any difficulties, or perhaps you have some suggestions for us, please do not hesitate to reach out and we will gladly work with you.
Requirements
To make use of our API integration we require the following:
- A valid shipping account with BEX.
- The creation of an integration user identity under which you will transact over the API’s
- For security sensitive data requests, a valid token.
Note: If you have multiple shipping accounts you are not required to transact under multiple integration identities (“tokens”). Our platform supports the assignment of multiple shipping accounts to a single token, making integration across multiple business units easier.
Request/Response Format
Our API endpoints will respond with JSON content accompanied by a 200 OK
HTTP status. This includes any possible processing errors. Please see the Errors section.
Errors
You can find the errors specific to each API endpoint under its dedicated API topic.
API errors are communicated through the ex
(short for exception) property inside the response body. Please inspect this property for any possible errors that may have occurred.
A possible problematic response could look as follows:
- HTTP status code:
200 OK
- API response:
"ex": "Account number is not registered for transactions with this token."
Above: Note the 200 OK status whilst receiving an error "ex": response
In instances where there is a technical breakdown in the processing of the API request, such as querying the wrong endpoint address, you will receive the appropriate http status code such as a 404
.
Parameters
All parameters as required by the relevant API endpoint may be provided in either query string format (e.g. POST /getcustomquicktracking_v3?ref=test123
) OR as part of the body in JSON
format.
The parameters the relevant API endpoint requires are discussed in its relevant section.
Tools
You are free to use any IDE or tool that satisfies your needs. We do recommend the use of POSTMAN to test your requests to our server.
Other tools you could also look at are:
- Insomnia - Cross-platform GraphQL and REST client, available for Mac, Windows, and Linux.
- Postman - Cross-platform REST client, available for Mac, Windows, and Linux.
- RequestBin - Allows you test webhooks.
- Hookbin - Another tool to test webhooks.
Authentication
Authentication against the BEX API ecosystem is a two-part process and is required to prevent access to confidential client data. The process involves the generation of your API security token which is described in the next section. This token is then included in the HTTP headers of your API calls and serves to identify and authenticate you on our platform.
API's that serve non-sensitive client data such as our quick waybill tracking do not require your token to be present in the API call and can be called anonymously.
Login
Overview
The login service serves as the starting point from which a valid session token will be generated. You call this endpoint to authenticate yourself using your BEX credentials.
The BEX platform will validate your request and if successful, generate and return in the response object a token contained in the value
attribute.
Endpoint
The authentication endpoint is located at https://api.bex.co.za/api/service/login
.
Ensure special characters are URL encoded.
{
"username": "your_bex_username",
"password": "your_bex_password"
}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>BEX API Login Example</title>
<script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
</head>
<body>
<script type="text/javascript">
var user = encodeURIComponent('username'); //encode the username
var pass = encodeURIComponent('password'); //encode the password
jQuery.support.cors = true; //this will enable cross domain access for all services
//call the service using jQuery ajax and passing the parameters
$.ajax({
url: 'https://api.bex.co.za/api/service/login?userName=' + user + '&password=' + pass,
type: 'POST',
async: false, //No need for async
success: function (d) {
if (d.ex) {
alert('An error has occurred. ' + d.ex);
return;
}
alert('Your token is ' + d.value);
}
});
</script>
</body>
</html>
Parameters
Parameter | FieldType | Required | Description |
---|---|---|---|
username | String | Yes | Your integration account username. |
password | String | Yes | Your integration account password (case sensitive). |
preferAlternateToken | Boolean | No | Set to true if so required by the documentation. |
Response
Sample json response.
{
"id": 19696,
// Here's the token -------------------------------------------------------
"value": "dOdw98ycsih298749bd6MbZre8o7tt3r3nhfowo8dnBDpEJvkzcQG1vb44sy",
// ------------------------------------------------------------------------
"isStrongPassword": "True",
"isEmailVerified": "True",
"email": "your.email@company.com",
"signalREnabled": "False",
"allowApiLogin": "False"
}
The response body is structured as follows:
Attribute | Type | Description |
---|---|---|
id | Int | An internal BEX ID used to identify your user. |
value | String | Your unique and private token. |
isStrongPassword | String | Confirms whether your password meets our complexity requirements. |
isEmailVerified | String | Your email address is used to recover forgotten passwords or lost tokens and needs to be verified before transactions are possible. |
String | The recovery email address against which this token is registered. | |
signalRenabled | Boolean | For internal use only. |
allowApiLogin | Boolean | For internal use only. |
With your token now generated you can proceed to call our security restricted API's.
Tracking
Overview
You can query our courier platform for up-to-the-minute tracking information pertaining to your shipments. We have exposed a lightweight tracking service that returns non-sensitive tracking events for the shipments queried. To cater for customers wishing to display this tracking information on their own websites we include a schema definition in the tracking response that can be used to bind the fields to a client grid implemented on your website.
The service allows for the submission of up to ten (10) waybills and/or customer reference numbers per API call.
You may submit a combination of waybill and/or reference numbers in a single call.
Endpoint
The tracking API is located at https://api.bex.co.za/api/service/waybillquicktrackingv3customtreeview
.
You can complete the request by using query parameters, for example /api/service/waybillquicktrackingv3customtreeview?searchItems=Item_1,Item_2,Item_3
or by submitting a JSON request.
Sample json request
{
"searchItems": "ACP1055352,MDH42240,MA210214"
}
Parameters
Parameter | Type | Required | Description |
---|---|---|---|
searchItems | String | Yes | A comma separated list of items to be searched. Can be either a waybill, reference number, or a combination of both. |
Response
Sample json tracking response
{
"id": 0,
"items": [
// first object is our data-definition object, identified by the groupingIndex == -1.
{
"id": 0,
"dataTypeId": 4,
"groupingIndex": -1,
"headerName": "Tracking",
"col1": "Date",
"col2": "Waybill",
"col3": "Senders Ref",
"col4": "Tracking Description",
"col5": "User",
"internalRef": "Internal Ref"
},
// subsequent objects are the tracking events themselves, identified by the groupingIndex == 0
{
"id": 13751791,
"dataTypeId": 2,
"headerName": "Tracking",
"col1": "06-09-2021 08:03",
"col2": "ACP1055352",
"col3": "740493",
"col4": "Delivered to PRTER ME.A at 08:26 on 03 Sep 2021",
"col5": "SAMMY GROBLER",
"internalRef": "13751791",
"groupingIndex": 0
},
{
"id": 13751791,
"dataTypeId": 2,
"headerName": "Tracking",
"col1": "03-09-2021 08:26",
"col2": "",
"col3": "740493",
"col4": "Driver confirmed successful delivery",
"col5": "JACKIE MATHOLE",
"internalRef": "13751791",
"groupingIndex": 0
},
{
"id": 13751791,
"dataTypeId": 2,
"headerName": "Tracking",
"col1": "03-09-2021 07:06",
"col2": "",
"col3": "740493",
"col4": "Loaded onto vehicle for delivery",
"col5": "MORAKA RAMOKGOPA",
"internalRef": "13751791",
"groupingIndex": 0
},
{
"id": 13751791,
"dataTypeId": 2,
"headerName": "Tracking",
"col1": "03-09-2021 06:37",
"col2": "",
"col3": "740493",
"col4": "Arrived into PRY depot",
"col5": "MORAKA RAMOKGOPA",
"internalRef": "13751791",
"groupingIndex": 0
},
{
"id": 13751791,
"dataTypeId": 2,
"headerName": "Tracking",
"col1": "02-09-2021 21:35",
"col2": "",
"col3": "740493",
"col4": "Departed JNB depot for PRY branch",
"col5": "GEOFREY SEANEGO",
"internalRef": "13751791",
"groupingIndex": 0
},
{
"id": 13751791,
"dataTypeId": 2,
"headerName": "Tracking",
"col1": "02-09-2021 17:49",
"col2": "",
"col3": "740493",
"col4": "Shipment integrated to courier system",
"col5": "JONATHAN DAVIS",
"internalRef": "13751791",
"groupingIndex": 0
}
]
}
The response object contains an items array. The first object in this array is our data definition object. It’s purpose is to communicate the bindings of the column names to their column numbers in the subsequent objects. The purpose of this first object is to allow you to use this information for the binding up of the data to a client grid on your website if you wish.
Attribute | Type | Description |
---|---|---|
id | int | Our internal ID which has no relevance to you. |
items | array | An array containing the tracking event objects. |
The objects contained in the items array are comprised as follows:
Attribute | Type | Description |
---|---|---|
id | int | Our internal ID which has no relevance to you. |
dataTypeId | int | Internal to BEX and of no use to you. |
groupingIndex | int | A tree hierarchy index used to identify parent objects from children. |
headerName | string | The name of the entity being returned, in this case “Tracking”. |
col1 | string | Column1, typically the DateTime the event occurred. |
col2 | string | Column2, typically the Waybill Number of the shipment. |
col3 | string | Column3, typically the Customer reference supplied for the shipment. |
col4 | string | Column4, typically the description of the Tracking event. |
col5 | string | Column5, the BEX member of staff who recorded the event. |
internalRef | string | Our internal ID which has no relevance to you. |
Quoting
Overview
The BEX quoting service allows you to view dynamic shipping pricing across our range of delivery services. By providing us with your shipment addressing information we can return a pricing spread as well as delivery estimations which you can use to make a more informed shipping choice.
Endpoint
The quoting API endpoint is located at https://api.bex.co.za/api/service/getquoteservicedeliverypricingv2
.
Sample json quoting message body
{
"dispatchDate": "2021-09-03",
"totalWeight": "10",
"dimMass": "25000",
"accountNumber": "316085",
"originSuburb": "Ballito",
"originPostCode": "4319",
// If you provide coordinates as well as location names we will favour the coordinates
"originLatCoord": -29.1234,
"originLongCoord": "18.5099",
"destinationLatCoord": "-26.1327",
"destinationLongCoord": "28.1957"
}
Parameters
To offer an accurate price we require the following parameters from you.
Parameter | Type | Description |
---|---|---|
dispatchDate | Date | The intended date of shipping. |
totalWeight | Decimal | The total actual weight in kilograms (as placed on a scale) of all of the goods to be shipped |
dimMass | Decimal | The sum of the total cubic centimetres of all of the goods to be shipped. (length x breadth x height) + (length x breadth x height) |
accountNumber | String | Your shipping account number with BEX. |
originSuburb | String | The suburb from where we will collect your shipment |
originPostCode | String | The postcode of the suburb from where we will collect your shipment. |
destinationSuburb | String | The suburb where we will deliver your shipment |
destinationPostCode | String | The postcode of the suburb where we will deliver your shipment. |
originLatCoord | Decimal | The GPS latitude (north-south position) of your collection location, expressed in decimal notation. |
originLonCoord | Decimal | The GPS longitude (east-west position) of your collection location, expressed in decimal notation. |
destinationLatCoord | Decimal | The GPS latitude (north-south position) of your delivery location, expressed in decimal notation. |
destinationLonCoord | Decimal | The GPS longitude (east-west position) of your delivery location, expressed in decimal notation. |
We are flexible as to how you supply the addressing.
// Snippet 1: Just coordinates
{
"originLatCoord":"-26.2462726",
"originLonCoord":"28.0969053",
"destinationLatCoord":"-26.5372133",
"destinationLonCoord":"29.1284535",
"totalWeight":"43",
"dimMass":"224000"
}
// Snippet 2: Just location names
{
"originSuburb":"Steeledale",
"originPostCode":"2197",
"destinationSuburb":"Secunda",
"destinationPostCode":"2302",
"totalWeight":"43",
"dimMass":"224000"
}
// Snippet 3: A combination of both
{
"originLatCoord":"-26.2462726",
"originLonCoord":"28.0969053",
"destinationSuburb":"Secunda",
"destinationPostCode":"2302",
"totalWeight":"43",
"dimMass":"224000"
}
Calculation of the delivery charges
In courier we use the greater of the actual weight or the volume of the goods to be shipped. Basically, we charge by weight OR by the physical size of the goods, whichever is greater (aircraft get expensive when flying empty boxes around).
These 2 parameters are:
totalWeight
The total weight in kilograms of all the goods on this orderdimMass
The sum of the total cubic centimetres of all the goods on this order
Example: A customer order is going from Sandton to Secunda. The order is made up of 2 boxes as follows:
- A stock 1 box (40cm x 40cm x 20cm) and weighing 18kgs
- A stock 4 box (80cm x 80cm x 30cm) and weighing 25kgs.
Looking at the code snippets on the right, you can see that the quotation request is to be structured as follows:
"totalWeight": "43"
"dimMass": "224000"
(40x40x20) + (80x80x30) = 224,000
For the addressing, first prize we would like the co-ordinates of the collection and delivery locations (json snippet 1), otherwise you can pass us the suburb and postcode of the collection and delivery locations (snippet 2 or 3).
Response
json quoting response.
{
"id": 0,
"items": [
{
"id": 0,
"customerName": "BOEING AIRCRAFT COMPANY",
"serviceId": 449,
"serviceCodeStr": "ONC",
"serviceNameStr": "OVERNIGHT COURIER",
"originSuburb": "AGGENEYS",
"originCity": "AGGENEYS",
"originRateCategory": "REMOTE",
"originBranchStr": "CAPE TOWN",
"originHub": "CPT",
"originLocationCode": "",
"destinationSuburb": "CROYDON",
"destinationCity": "KEMPTON PARK",
"destinationRateCategory": "MAJOR",
"destinationBranchStr": "JOHANNESBURG",
"destinationHub": "JNB",
"destinationLocationCode": "",
"estimatedTransitDays": 4,
"scheduledTransitDays": 4,
"estimatedHolidays": 0,
"scheduledHolidays": 0,
"scheduleBy": "2021-10-05 16:00:00",
"estimatedCollectionDateAndTime": "2021-10-05 18:00:00",
"latestCollectionDateAndTime": "2021-10-05 18:00:00",
"estimatedDeliveryDateAndTime": "2021-10-09 10:00:00",
"scheduledDeliveryDateAndTime": "2021-10-09 14:00:00",
"isDeliveryGuaranteed": "True",
"surchargeAddons": "SURSAT",
"customerChargeableWeight": 10,
"baseRateTotal": 0.00,
"fuel": 0.00,
"insuranceRequired": 0.00,
"otherSurcharges": 0.00,
"surchargeTotal": 0.00,
"subTotal": 0.00,
"vat": 0.00,
"totalCharges": 0.00
},
{
"id": 0,
"customerName": "BOEING AIRCRAFT COMPANY",
"serviceId": 449,
"serviceCodeStr": "ONC",
"serviceNameStr": "OVERNIGHT COURIER",
"originSuburb": "AGGENEYS",
"originCity": "AGGENEYS",
"originRateCategory": "REMOTE",
"originBranchStr": "CAPE TOWN",
"originHub": "CPT",
"originLocationCode": "",
"destinationSuburb": "CROYDON",
"destinationCity": "KEMPTON PARK",
"destinationRateCategory": "MAJOR",
"destinationBranchStr": "JOHANNESBURG",
"destinationHub": "JNB",
"destinationLocationCode": "",
"estimatedTransitDays": 4,
"scheduledTransitDays": 4,
"estimatedHolidays": 1,
"scheduledHolidays": 1,
"scheduleBy": "2021-10-03 16:00:00",
"estimatedCollectionDateAndTime": "2021-10-03 18:00:00",
"latestCollectionDateAndTime": "2021-10-03 18:00:00",
"estimatedDeliveryDateAndTime": "2021-10-08 10:30:00",
"scheduledDeliveryDateAndTime": "2021-10-08 11:00:00",
"isDeliveryGuaranteed": "True",
"surchargeAddons": "",
"customerChargeableWeight": 10,
"baseRateTotal": 0.00,
"fuel": 0.00,
"insuranceRequired": 0.00,
"otherSurcharges": 0.00,
"surchargeTotal": 0.00,
"subTotal": 0.00,
"vat": 0.00,
"totalCharges": 0.00
}
]
}
In the response object you will find an items array of objects containing the different service and pricing options available for the shipment routing you have requested.
The objects in the items array are comprised as follows:
Attribute | Type | Description |
---|---|---|
Id | Int | An internal BEX field. |
customerName | String | Your account name with BEX. |
serviced | Int | An internal id used to identify the shipping service in question. |
serviceCodeStr | String | The shipping service code. |
serviceNameStr | String | The full name of the shipping service. |
originSuburb | String | The suburb we calculated from where your shipment will be sent. |
originCity | String | The city from where your shipment will be sent. |
originRateCategory | String | The billing category of the location from where your shipment will be sent. |
originBranchStr | String | The name of the BEX branch who will handle the collection of your shipment. |
originHub | String | The 3 letter code of the BEX branch that will collect your shipment. |
originLocationCode | String | The 3 letter town code of the location from where your shipment will be sent. |
destinationSuburb | String | The suburb where your delivery will take place. |
destinationCity | String | The city where your delivery will take place. |
destinationRateCategory | String | The billing category of the location where your shipment will be delivered. |
destinationBranchStr | String | The name of the BEX branch that will handle the delivery of your shipment. |
destinationHub | String | The 3 letter code of the BEX branch that will deliver your shipment. |
destinationLocationCode | String | The 3 letter town code of the location where your shipment will be delivered. |
estimatedTransitDays | Int | The number of business days we believe it will take to complete delivery of your shipment. |
scheduledTransitDays | Int | The number of business days we may take to complete the delivery of your shipment, based on the service level of the shipping service. |
estimatedHolidays | Int | The number of non-working days falling within the period of our delivery lead-time estimation. |
scheduledHolidays | Int | The number of non-working days falling within the period of our service sla lead-time. |
scheduleBy | Datetime | The cut-off time by which you need to capture your shipping request into our system to ensure the delivery lead times are achievable. |
estimatedCollectionDateAndTime | Datetime | The estimated date and time we can collect your shipment at its origin location to commence the shipping process. |
latestCollectionDateAndTime | DateTime | Our operational cut-off time after which we are unable to collect your shipment. |
estimatedDeliveryDateAndTime | DateTime | Our estimated date and time of delivery of your shipment. |
scheduledDeliveryDateAndTime | DateTime | The scheduled date and time of delivery of your shipment. |
isDeliveryGuaranteed | String | Some delivery schedules are guaranteed, depending on the shipping service selected. |
surchargeAddons | String | An optional service-addon which if selected, will provide additional options such as a Saturday or After hours delivery. |
Waybill
Overview
This service is used to create a waybill shipping entry into the BEX courier database. This allows BEX customers to directly interface their shipping requests and helps to eliminate errors as a result of manual data capture or misinterpretation of written content on shipping documentation. The service allows for the communication of a number of shipping options, such as:
- Full addressing of both the sending and receiving parties
- Additional shipping references against which the shipping entry is to be tagged
- Multiple packages and their associated dimensions and weights
- Special handling requests such as after-hour, Saturday or public holiday deliveries
Endpoint
The waybill API endpoint is located at https://api.bex.co.za/api/service/submitwaybillv4
.
The parameter requirements for this API call have been broken down into five sub-sections, namely:
- Basic Details
- Addresses - Contains parameters for the construction and routing information of the Waybill
- Dimensioning
- Additional References
- Printing Options
Sample json message body.
{
"waybillNumber": "", //Leave empty to auto generate OR enter a custom waybill number.
"autoGenerateWaybillNumber": true, //Set this to _false_ if you are providing a custom waybill number.
"waybillDate": "YYYY-MM-DD",
"accountNumber": "your_bex_account_number",
"serviceCode": "ONC", //or whichever service you may require
"sendersRef": "a reference for the sender", //(optional)
"instructions": "special instructions for the courier", //(optional)
"insuranceRequired": false, //Set to _true_ if you require insurance
"insuranceValue": 0, //(optional) Specify a value only if _insuranceRequired_ is set to _true_.
"senderCompany": "Doe Digital Works", //(optional)
"senderFloorBuilding": "17 Cheltham House, Floor 3", //(optional)
"senderStreet": "123 John Doe Avenue",
"senderSuburb": "Ross Kent South",
"senderCity": "Odendaalsrus",
"senderPostCode": "9800",
"senderProvince": "Free State", //(optional)
"senderContactName": "John Doe", //(optional)
"senderMobile": "1234567890", //(optional)
"senderEmail": "john@domain.com", //(optional)
"senderTel": "1234567890", //(optional)
"senderTelHome": "1234567890", //(optional)
"senderLat": sender_latitude, //(optional) Although, we encourage the use of coordinates!
"senderLon": sender_longitude, //(optional) Although, we encourage the use of coordinates!
"receiverCompany": "Jane Flower Works", //(optional)
"receiverFloorBuilding": "Floor B-4", //(optional)
"receiverStreet": "166 Wringham Drive",
"receiverSuburb": "Table View",
"receiverCity": "Cape Town",
"receiverPostCode": "7441",
"receiverProvince": "Western Cape", //(optional)
"receiverContactName": "Jane Doe", //(optional)
"receiverMobile": "1234567890", //(optional) If a number is provided, delivery notifications will be sent to this number.
"receiverEmail": "jane@domain.com", //(optional) If an email is provided, delivery notifications will be sent to this address.
"receiverTel": "1234567890", //(optional)
"receiverTelHome": "1234567890", //(optional)
"receiverLat": receiver_latitude, //(optional) Although, we encourage the use of coordinates!
"receiverLon": receiver_longitude, //(optional) Although, we encourage the use of coordinates!
"returnParcelPrintingText": false, //(optional) Returns EPL thermal label data as part of the response for you to send to the thermal printer.
"printParcels": false, //(optional) If _true_, parcels are printed to the thermal label printer after submission using the URL specified.
"printUrl": "", //(optional) Specify the thermal label printer URL to print to IF _printParcels=true_.
"printParcelsPrintNode": false, //(optional) Specify _true_ to use PrintNode to print the labels if _printParcels=true_.
"dimensions": [{
"dimension1": length_cm,
"dimension2": breadth_cm,
"dimension3": height_cm,
"weight": parcel_weight_kg,
"reference": "" //(optional) A parcel description
}],
"additionalReferences": [{
"value": "reference_1"
}, {
"value": "reference_2"
}]
}
jQuery Waybill Submission Example.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>BEX waybill API example</title>
<script src="http://code.jquery.com/jquerylatest.min.js" type="text/javascript"></script>
</head>
<body>
<script type="text/javascript">
//********** Login Service Call ********************
var tokenStr = '';
var user = encodeURIComponent('someusername'); //encode the username
var pass = encodeURIComponent('somepassword'); //encode the password
var host = 'https://api.bex.co.za';
//********** Submit Waybill Service Call ***********
var postData = {};
//BASIC
postData.waybillNumber = 'IT2701152404'; //or leave empty and set [autoGenerateWaybillNumber=true]
postData.autoGenerateWaybillNumber = false; //set to [false] if you're supplying a waybill number.
postData.waybillDate = '20150127';
postData.accountNumber = '111983';
postData.serviceCode = 'NCA';
postData.sendersRef = 'WE HAVE A REFERENCE';
postData.instructions = 'DO NOT INVERT COMPONENT';
//INSURANCE OPTIONS
postData.insuranceRequired = false; //Set to true if you need insurance.
postData.insuranceValue = 0; //Set the waybill value here if [insuranceRequired=true].
//SENDER DETAILS
//--Address
postData.senderCompany = 'THE BOEING COMPANY';
postData.senderFloorBuilding = 'UNIT 6 ASSEMBLY BUILDING';
postData.senderStreet = '21 AIRCRAFT LANE';
postData.senderSuburb = 'MARLBORO';
postData.senderCity = 'SANDTON';
postData.senderPostCode = '2050';
postData.senderProvince = 'GAUTENG'; //optional
postData.senderLat = null; //optional BUT WE ENCOURAGE the use of COORDINATES. Enter the LATITUDE here, e.g. -26.123496410189606
postData.senderLon = null; //optional BUT WE ENCOURAGE the use of COORDINATES. Enter the LONGITUDE here, e.g. 28.205670867327754
//--Contact
postData.senderContactName = 'JONATHAN'; //optional
postData.senderMobile = '0821234321'; //optional
postData.senderEmail = 'JONATHAN@BOEING.COM'; //optional
postData.senderTel = '0119299700'; //optional
postData.senderTelHome = '0119299700'; //optional
//RECEIVER DETAILS
//--Address
postData.receiverCompany = 'THE AIRLINER CO';
postData.receiverFloorBuilding = '1ST FLOOR HEAD OFFICE';
postData.receiverStreet = '21 TULP STREET';
postData.receiverSuburb = 'MILNERTON';
postData.receiverCity = 'CAPE TOWN';
postData.receiverPostCode = '7401';
postData.receiverProvince = 'WESTERN CAPE'; //optional
postData.receiverLat = null; //optional BUT WE ENCOURAGE the use of COORDINATES. Enter the LATITUDE here, e.g. -26.123496410189606
postData.receiverLon = null; //optional BUT WE ENCOURAGE the use of COORDINATES. Enter the LONGITUDE here, e.g. 28.205670867327754
//--Contact
postData.receiverContactName = 'DEREK';
postData.receiverMobile = '0833211234';
postData.receiverEmail = 'DEREK@THEAIRLINER.COM';
postData.receiverTel = '0218641202';
postData.receiverTelHome = '0218641202';
//PARCELS AND DIMENSIONS
var dimensions = [];
var parcel1 = {};
parcel1.dimension1 = 40; //Length (cm)
parcel1.dimension2 = 30; //Breadth (cm)
parcel1.dimension3 = 1; //Height (cm)
parcel1.weight = 6; //Parcel Weight (nearest whole kg)
parcel1.reference = 'AIRFRAME COMPONENT'; //(optional)A reference for this parcel.
dims.push(parcel1);
//-->...continue adding the rest of the parcels
postData.dims = dims;
//ADDITIONAL REFERENCES []
var additionalRefs = [];
var ref1 = {};
ref1.value = "ORDER 22341";
additionalRefs.push(ref1);
//-->...continue adding the rest of your references.
postData.additionalReferences = additionalRefs;
//THERMAL LABEL PRINTING
postData.returnParcelPrintingText = false; //(optional) Returns EPL thermal label data as part of the response for you to send to the thermal printer.
postData.printParcels = false; //(Optional) When set to [true], parcels are printed to the thermal label printer after submission using the [printUrl] specified.
postData.printUrl = ''; //(optional) Specify the thermal label printer URL to print to IF [printParcels=true]
postData.printParcelsPrintNode = false; //(optional) Specify [true] to use PrintNode to print the labels if [printParcels=true].
//----- SUBMIT TO BEX -----//
//NOTE: If your call fails, it will still be communicated as 200 OK. Inspect the [ex] property in the response for error information.
jQuery.support.cors = true; //Enables cross-domain access to our services using jQuery ajax.
//--Step 1: Get your token.
$.ajax({
url: host + '/api/service/login?userName=' + user + '&password=' + pass,
type: 'POST',
async: false, //No need for async here.
success: function (d) {
if (d.ex) {
alert('We couldnt complete the request. ' + d.ex);
return;
}
tokenStr = data.value;
}
});
//--Step 2: Submit your waybill request.
$.ajax({
url: host + '/api/service/submitwaybillv4',
type: 'POST',
data: JSON.stringify(postData),
dataType: 'json',
contentType: 'application/json; charset=UTF8',
beforeSend: function (xhr) {
//Add your token to the header
xhr.setRequestHeader('token', tokenStr);
},
success: function (d) {
if (d.ex) {
document.write('We failed to submit your waybill. ' + d.ex);
return;
}
document.write('Your waybill was created successfully.');
}
});
</script>
</body>
</html>
Basic Details
Parameter | FieldType | Case | Required | Note |
---|---|---|---|---|
waybillNumber | String | No* | No | A unique waybill number. This field will be required if AutoGenerateWaybillNumber is set to false. |
autoGenerateWaybillNumber | Boolean | N/A | No* | If set to true, the BEX system will automatically generate a waybill number on your behalf. |
waybillDate | Date | No | No | Date of collection by BEX. Format yyyy-mm-dd |
accountNumber | String | No | Yes | |
sendersRef | String | No | No | For tracking, reporting and invoice requests |
instructions | String | No | No | Communicate additional info |
*waybillNumber
is auto-generated by BEX and communicated in the service response when making use of alternative shipping stationery options such as thermal labels or A4 waybills
Parameter | FieldType | Case | Required | Note |
---|---|---|---|---|
serviceCode | String | No | Yes | The service delivery priority code |
Service delivery code values are:
- SDX - Sameday Express
- DBD - Daybreak Delivery
- ONC - Overnight Courier
- NCA - Normal Cargo
- BUD - Budget Cargo
Sender Address
Parameter | FieldType | Case | Required | Note |
---|---|---|---|---|
senderCompany | String | No | No | Company from which to collect |
senderFloorBuilding | String | No | Yes | Floor, building, office park |
senderStreet | String | No | Yes | Street for shipment collection |
senderSuburb | String | No | Yes | Sender suburb |
senderCity | String | No | Yes | Sender town or city |
senderPostCode | String | No | Yes | Sender postcode |
senderProvince | String | No | No | Sender province. |
senderLat | Double | N/A | No | Sender coordinate latitude value (we encourage the use of these) |
senderLon | Double | N/A | No | Sender coordintae longitude value (we encourage the use of these) |
senderContactName | String | No | No | Contact person for collection/dispatch |
senderMobile | String | No | No | Contact person mobile number |
senderTel | String | No | No | Contact person telephone number |
senderTelHome | String | No | No | Contact person telephone number (home or alternate) |
senderEmail | String | No | No | Contact person email address |
Receiver Address
Parameter | FieldType | Case | Required | Note |
---|---|---|---|---|
receiverCompany | String | No | No | Receiver individual/company name |
receiverFloorBuilding | String | No | No | Floor and building address. |
receiverStreet | String | No | Yes | Receiver street number and name |
receiverSuburb | String | No | Yes | Receiver suburb. |
receiverCity | String | No | Yes | Receiver city or town. |
receiverPostCode | String | Yes | No | Receiver postcode |
receiverProvince | String | No | No | Receiver province. |
receiverLat | Double | N/A | No | Receiver coordinate latitude value (we encourage the use of these) |
receiverLon | Double | N/A | No | Receiver coordinate longitude value (we encourage the use of these) |
receiverContactName | String | No | No | Receiver name. |
receiverMobile | String | No | No | Useful for status updates |
receiverEmail | String | No | No | Useful for status updates |
receiverTel | String | No | No | Receiver telephone number |
receiverTelHome | String | No | No | Receiver telephone number (home or alternate). |
If you would like to add insurance cover on your waybill, complete these two fields:
Parameter | FieldType | Case | Required | Note |
---|---|---|---|---|
insuranceRequired | Boolean | No | No | true if insurance is required for this shipment. |
insuranceValue | Float/Double | No | Yes | Insured value, in ZAR. Omit this field if you do not require insurance. |
Dimensioning
For communication of parcel specific information for each package that comprises this waybill shipping request.
Parameter | FieldType | Case | Required |
---|---|---|---|
dimensions | Array | No | Yes |
The dimensions
array comprises:
FieldName | FieldType | Req | Description |
---|---|---|---|
dimension1 | Int | Yes | Length of the package, rounded up to the nearest whole centimeter |
dimension2 | Int | Yes | Width of the package, rounded up to the nearest whole centimeter |
dimension3 | Int | Yes | Height of the package, rounded up to the nearest whole centimeter |
weight | Float | Yes | Actual weight of package in kilograms |
reference | String | No | Tag this package with parcel specific information. |
Additional References
You can include additional references to the waybill for ease of reference and search.
Parameter | FieldType | Case | Required |
---|---|---|---|
additionalReferences | Array | No | No |
The additionalReferences
array comprises:
FieldName | FieldType | Req | Description |
---|---|---|---|
value | String | Yes | The reference number you want to add. |
Printing Options
These options should only be used if so advised by your sales representative or a BEX staff member. You may need additional information and/or setup before you may use these features.
Parameter | FieldType | Req | Required |
---|---|---|---|
returnParcelPrintingText | Boolean | No | Optional. If true , returns EPL thermal label data as part of the response for you to send to the thermal printer. |
printParcels | Boolean | No | Optional. If true , parcels are printed to the thermal label printer after submission using the printUrl specified. |
printUrl | String | No | Optional. Specify the thermal printer URL to print to IF printParcels=true . |
printParcelsPrintNode | Boolean | No | Optional. Specify true to use PrintNode to print the labels. |
Response
The base object will contain a property called items
which consists of an array holding a single object with the following properties:
Property | Description |
---|---|
waybillNumber | String value reflecting the unique BEX waybill number for this successful shipping request. |
baseRateTotal | Price of the rate card billing component for shipment. |
surchargesTotal | Price of the surcharges for additional service add-ons |
subTotal | The subtotal, excluding VAT, for the shipment routing requested. |
vat | The VAT portion of the pricing for the delivery. |
totalCharges | The grand total price for the waybill. |
volRatio | The volumetric ratio used during the volumetric weight calculation in the billing engine. This ratio is directly dependent on the service delivery priority requested. |
Submission Failures
Should your request fail, please inspect the ex
property as mentioned earlier in this document.
Waybill Downloads
Overview
As part of our service offering we allow you to download an e-waybill and any parcel labels associated with the waybill. The e-waybill is in A4 format and can be printed using any printer at your disposal.
Authentication
The stationery download service requires a different token than the one you have been using to access the other endpoints.
You will need to re-complete the authentication procedure but you are required to set the preferAlternateToken
to true
. This will result in a completely different token being issued for your request. You must use this new token for the stationery download service only.
eWaybill Download
You can download the e-waybill from https://api.bex.co.za/api/service/downloadewaybill
.
Supply only 1 waybill number per call.
{
"token": "`alternate_token`",
"waybillNumber": "waybill_number"
}
Parameters
Parameter | FieldType | Required | Description |
---|---|---|---|
token | String | Yes | The alternate_token received through the login service. |
waybillNumber | String | Yes | A single waybill number to lookup and produce a PDF for. |
Response
A successful response will be the resulting PDF document. If you are accessing it through a browser, the download will happen automatically.
If you decide to integrate using your own system or code, you will need to process the response accordingly.
Labels Downloads
You can download "thermal" labels from https://api.bex.co.za/api/service/downloadparcellabels
.
Example JSON body.
{
"token": "`alternate_token`",
"waybillNumbers": "waybill_number_1,waybill_number_2,waybill_number_3,..."
}
Parameters
Parameter | FieldType | Required | Description |
---|---|---|---|
token | String | Yes | The alternate_token received through the login service. |
waybillNumbers | String | Yes | A comma-seperated list of waybill numbers or parcel numbers to generate PDF labels for. |
Response
A successful response will be the resulting PDF document. If you are accessing it through a browser, the download will happen automatically.
If you decide to integrate using your own system or code, you will need to process the response accordingly.
Label Size
The downloadable label is sized at 10.08cm x 14.98cm and may be printed on any printer you have at your disposal.
Webhooks Guide
Getting Started
In order to use our WebHooks, you need to ensure that you have a valid and actively trading customer account registered with BEX. Once you have your account number, you will need to request a WebHook API key from our IT department. We will communicate this to you but it is your responsibility to ensure the secrecy of it.
Before we get started, please ensure that you understand and have implemented the necessary methods required to work with WebHooks.
WebHook Registration
The registration is a two-part process that requires two individual requests.
First, you need to register your WebHook with us by sending a POST request to our WebHook endpoint located at:
https://webhook.bex.co.za/api/webhooks/registrations
In the header of your request, you should add a new key pair that represents your WebHook API key:
{
“X-InsightWebHooks-Auth”: “Your API Key”
}
The request body is made up of:
{
WebHookUri: “your endpoint where the webhook will post”,
Secret: “A secret key that you generate”,
Description: “A description for your webhook”,
Filters: [ “filterA”, “filterB” ]
}
After you have made this request and you receive a 200 OK response, you need to send another request to:
https://webhook.bex.co.za/api/ActivateWebHook
Providing the request body with the following values:
{
webHookKey: “your API key”
}
A Word on Filters
We provide several different types of WebHook events and you can subscribe to one or all of them. If you want to subscribe to ALL events, then you should ensure that your code can differentiate between these types of events. In this case, you should provide an empty value for the Filters parameter i.e., Filters: []
If you would like to subscribe to certain events only, then you need to provide each event type you want to subscribe to, e.g. Filters: [ “event 1”, “event 2”, … ]
We provide the following filters/event types:
- pod
- attempted_delivery
- waybill_update
- waybill_note
- tracking_event
Unsubscribing
To unsubscribe from our WebHook, send a POST request to: https://webhook.bex.co.za/api/UnsubscribeWebHook
The request body should consist of:
{
webHookKey: “your API key”
}
Filters/Event Types
Basic Payload Structure
The basic payload structure for our WebHook would look like this:
{
“Id”: “a unique id for this message”,
“Attempt”: 1,
“Properties”: { },
“Notifications”: [{
“Action”: “tracking_type”,
…
}]
}
As part of the request to your WebHook endpoint, we also include a special header:
- Header Key: ms-signature
- Header Value: sha256=calculatedHashValueGoesHere
Notifications Property
The notifications property will contain the type of notification you are receiving (“Action”) followed by the list of properties relating to that specific event type. These properties are listed below for your convenience.
Verifying WebHook Signature
Since we are only sending you a request with a payload and that we have no knowledge of your infrastructure, the message will be sent “anonymously” and won’t provide any mechanism to allow authentication to a closed system. It is your responsibility to ensure that the endpoint you specify accepts anonymous users and that you perform message validation as an alternative to authentication.
Every WebHook request that we send will have the special ms-signature header key containing the hash of the body we’ve sent. You should calculate the signature using the supplied secret (during registration) based on the body of the message you’ve received from us.
A C# example of calculating the signature would be:
var secret = Encoding.UTF8.GetBytes(secretYouProvidedDuringRegistration);
using (var hasher = new HMACSHA256(secret))
{
var serializedBody = body.ToString();
request.Content = new StringContent(serializedBody, Encoding.UTF8, “application/json”);
var data = Encoding.UTF8.GetBytes(serializedBody);
var sha256 = hasher.ComputeHash(data);
var headerValue = EncodingUtilities.ToHex(sha256);
}
Event Types/Payloads
Each event will be sent using the basic payload structure and contain different properties for each type of event.
Tracking Events
{
“Action”: “tracking_event”,
“WaybillNumber”: “12345”,
“TrackingDate”: “2021-07-12 11:59:25”,
“TrackingUser”: “John Doe”,
“TrackingDescription”: “tracking description”,
“TrackingReference”: “some reference”,
“TrackingTypeId”: -1
}
POD
{
“Action”: “tracking_event”,
“WaybillNumber”: “12345”,
“TrackingDate”: “2021-07-12 11:59:25”,
“TrackingUser”: “John Doe”,
“TrackingDescription”: “tracking description”,
“TrackingReference”: “some reference”,
“TrackingTypeId”: -1
} {
“Action”: “pod”,
“WaybillNumber”: “12345”,
“PODDate”: “2021-07-12”,
“PODTime”: “11:34:25”,
“Signee”: “Jane Doe”,
“PODPieces”: 1
}
Attempted Delivery
{
“Action”: “attempted_delivery”,
“WaybillNumber”: “12345”,
“AttemptedDate”: “2021-07-12”,
“AttemptedTime”: “11:12:16”,
“NonDeliveryReason”: “Wrong route”,
“DriverComment”: “driver”
}
Waybill Note
{
“Action”: “waybill_note”,
“PostedBy”: “John Doe”,
“NoteDate”: “2021-07-12 12:08:16”,
“NoteText”: “Customer will phone when home”
}
Waybill Update
{
“Action”: “waybill_update”,
“WaybillNumber”: “12345”,
“Service”: “ONC”,
“InsuranceRequired”: false,
“InsuranceValue”: 0,
“ActualWeight”: 1.0,
“VolumetricWeight”: 1.0,
“ChargeableWeight”: 1.0,
“BaseRateTotal”: 25,
“SurchargesTotal”: 17.50,
“SubTotal”: 42.50,
“Vat”: 6.38,
“TotalCharges”: 48.88,
“Parcels”: [
{
“ParcelBarcodeNumber”: “P1”,
“Dimension1”: 40,
“Dimension2”: 30,
“Dimension3”: 1,
“Weight”: 1.0,
“ParcelDescription”: “”
}, …
]
}
Tracking Event ID Descriptions:
Tracking Type ID | Description |
---|---|
1 | Shipment handed to courier. |
4 | Shipment departed XYZ branch/depot. |
5 | Shipment arrived at XYZ branch/depot. |
7 | Shipment loaded onto vehicle for delivery. |
9 | Delivery attempt failed because (reason variable) |
13 | Shipment integrated into our system. |