less than a minute read • Updated 12 minutes ago
Get Live Shipping Rates with Postmen
Connect your Postmen account to your Foxy store with the Custom Shipping Code feature to fetch live rates from Postmen's supported carriers at checkout.
Overview
Postmen (by AfterShip) returns rates from multiple carriers through one API. If you need rates from carriers that Foxy does not natively support, you can fetch them from your Postmen account using Foxy's Custom Shipping Code feature. This article shows you the code to connect to Postmen and how to install it on your store.
Before You Start
You will need:
A Postmen account with an API key. Postmen provides separate Production and Testing keys; pick the one that matches the environment you want.
One or more shipper accounts created in Postmen (Postmen dashboard, under Shipper Accounts). Make sure they belong to the same environment (Production or Testing) as your API key.
Custom Shipping Code enabled on your store. See Add Custom Shipping Code.
At least one product category set to a delivery type of "Shipped using live shipping rates". See Shipping and Categories.
The Shipping Code
This code builds a rate request from the cart payload, loops the cart items into the parcel, posts the request to the Postmen rates endpoint, then adds each returned rate to the cart. Postmen expects 3 letter (ISO 3166 alpha 3) country codes, so the included country_to_3 helper converts Foxy's 2 letter codes. Set the options at the top to match your account and packaging.
// Set to the API key of your Postmen account
let api_key = 'YOUR-POSTMEN-API-KEY';
// Set to false to use test shipper accounts
let is_production = true;
// Shipper accounts
let shipper_accounts = [
{ 'id': 'SHIPPER-ACCOUNT-ID-1' },
{ 'id': 'SHIPPER-ACCOUNT-ID-2' }
];
// Weight units for the store ('lb' or 'kg')
let weight_unit = 'lb';
// Length units for the store ('in' or 'cm')
let length_unit = 'in';
// Parcel dimensions
let parcel_width = 20,
parcel_height = 10,
parcel_depth = 10;
const shipment = cart['_embedded']['fx:shipment'];
let payload = {
'async': false,
'shipper_accounts': shipper_accounts,
'shipment': {
'parcels': [
{
'box_type': 'custom',
'weight': {
'value': shipment['total_weight'],
'unit': weight_unit
},
'dimension': {
'width': parcel_width,
'height': parcel_height,
'depth': parcel_depth,
'unit': length_unit
},
'items': [
// Populated below
]
}
],
'ship_from': {
'state': shipment['origin_region'],
'postal_code': shipment['origin_postal_code'],
'country': country_to_3(shipment['origin_country']),
},
'ship_to': {
// Populated below
'country': country_to_3(shipment['country']),
'type': shipment['is_residential'] ? 'residential' : 'business'
}
},
'is_document': false
};
// Ship to address
if (shipment['first_name']) {
payload.shipment.ship_to['contact_name'] = shipment['first_name'] + " " + shipment['last_name'];
}
if (shipment['address1']) {
payload.shipment.ship_to['street1'] = shipment['address1'];
}
if (shipment['city']) {
payload.shipment.ship_to['city'] = shipment['city'];
}
if (shipment['region']) {
payload.shipment.ship_to['state'] = shipment['region'];
}
if (shipment['postal_code']) {
payload.shipment.ship_to['postal_code'] = shipment['postal_code'];
}
// Items
for (let p in cart['_embedded']['fx:items']) {
let item = cart['_embedded']['fx:items'][p];
let item_details = {
'description': item.name,
'quantity': item.quantity,
'price': {
'amount': item.price,
'currency': cart.currency_code
},
'weight': {
'value': item.weight,
'unit': weight_unit
}
}
if (item.sku) {
item_details['sku'] = item.code
}
payload.shipment.parcels[0].items.push(item_details);
}
try {
const endpoint = is_production ? 'https://production-api.postmen.com/v3' : 'https://sandbox-api.postmen.com/v3'
const response = await fetch(endpoint + '/rates', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
"postmen-api-key": api_key,
"Content-Type": "application/json"
}
});
const data = await response.json();
if (data.meta.code != 200) {
console.log(data.meta)
}
for (let r = 0; r < data.data.rates.length; r++) {
const rate = data.data.rates[r];
if (rate.service_name) {
rates.add(10000+r, rate.total_charge.amount, '', rate.service_name);
} else if (rate.error_message) {
console.log(rate.error_message);
}
}
} catch(err) {
console.log(err);
}
function country_to_3(country) {
const iso3166_3 = {
AF: 'AFG', AX: 'ALA', AL: 'ALB', DZ: 'DZA', AS: 'ASM', AD: 'AND', AO: 'AGO', AI: 'AIA', AQ: 'ATA', AG: 'ATG', AR: 'ARG', AM: 'ARM', AW: 'ABW', AU: 'AUS', AT: 'AUT', AZ: 'AZE', BS: 'BHS', BH: 'BHR', BD: 'BGD', BB: 'BRB', BY: 'BLR', BE: 'BEL', BZ: 'BLZ', BJ: 'BEN', BM: 'BMU', BT: 'BTN', BO: 'BOL', BA: 'BIH', BW: 'BWA', BV: 'BVT', BR: 'BRA', VG: 'VGB', IO: 'IOT', BN: 'BRN', BG: 'BGR', BF: 'BFA', BI: 'BDI', KH: 'KHM', CM: 'CMR', CA: 'CAN', CV: 'CPV', KY: 'CYM', CF: 'CAF', TD: 'TCD', CL: 'CHL', CN: 'CHN', HK: 'HKG', MO: 'MAC', CX: 'CXR', CC: 'CCK', CO: 'COL', KM: 'COM', CG: 'COG', CD: 'COD', CK: 'COK', CR: 'CRI', CI: 'CIV', HR: 'HRV', CU: 'CUB', CY: 'CYP', CZ: 'CZE', DK: 'DNK', DJ: 'DJI', DM: 'DMA', DO: 'DOM', EC: 'ECU', EG: 'EGY', SV: 'SLV', GQ: 'GNQ', ER: 'ERI', EE: 'EST', ET: 'ETH', FK: 'FLK', FO: 'FRO', FJ: 'FJI', FI: 'FIN', FR: 'FRA', GF: 'GUF', PF: 'PYF', TF: 'ATF', GA: 'GAB', GM: 'GMB', GE: 'GEO', DE: 'DEU', GH: 'GHA', GI: 'GIB', GR: 'GRC', GL: 'GRL', GD: 'GRD', GP: 'GLP', GU: 'GUM', GT: 'GTM', GG: 'GGY', GN: 'GIN', GW: 'GNB', GY: 'GUY', HT: 'HTI', HM: 'HMD', VA: 'VAT', HN: 'HND', HU: 'HUN', IS: 'ISL', IN: 'IND', ID: 'IDN', IR: 'IRN', IQ: 'IRQ', IE: 'IRL', IM: 'IMN', IL: 'ISR', IT: 'ITA', JM: 'JAM', JP: 'JPN', JE: 'JEY', JO: 'JOR', KZ: 'KAZ', KE: 'KEN', KI: 'KIR', KP: 'PRK', KR: 'KOR', KW: 'KWT', KG: 'KGZ', LA: 'LAO', LV: 'LVA', LB: 'LBN', LS: 'LSO', LR: 'LBR', LY: 'LBY', LI: 'LIE', LT: 'LTU', LU: 'LUX', MK: 'MKD', MG: 'MDG', MW: 'MWI', MY: 'MYS', MV: 'MDV', ML: 'MLI', MT: 'MLT', MH: 'MHL', MQ: 'MTQ', MR: 'MRT', MU: 'MUS', YT: 'MYT', MX: 'MEX', FM: 'FSM', MD: 'MDA', MC: 'MCO', MN: 'MNG', ME: 'MNE', MS: 'MSR', MA: 'MAR', MZ: 'MOZ', MM: 'MMR', NA: 'NAM', NR: 'NRU', NP: 'NPL', NL: 'NLD', AN: 'ANT', NC: 'NCL', NZ: 'NZL', NI: 'NIC', NE: 'NER', NG: 'NGA', NU: 'NIU', NF: 'NFK', MP: 'MNP', NO: 'NOR', OM: 'OMN', PK: 'PAK', PW: 'PLW', PS: 'PSE', PA: 'PAN', PG: 'PNG', PY: 'PRY', PE: 'PER', PH: 'PHL', PN: 'PCN', PL: 'POL', PT: 'PRT', PR: 'PRI', QA: 'QAT', RE: 'REU', RO: 'ROU', RU: 'RUS', RW: 'RWA', BL: 'BLM', SH: 'SHN', KN: 'KNA', LC: 'LCA', MF: 'MAF', PM: 'SPM', VC: 'VCT', WS: 'WSM', SM: 'SMR', ST: 'STP', SA: 'SAU', SN: 'SEN', RS: 'SRB', SC: 'SYC', SL: 'SLE', SG: 'SGP', SK: 'SVK', SI: 'SVN', SB: 'SLB', SO: 'SOM', ZA: 'ZAF', GS: 'SGS', SS: 'SSD', ES: 'ESP', LK: 'LKA', SD: 'SDN', SR: 'SUR', SJ: 'SJM', SZ: 'SWZ', SE: 'SWE', CH: 'CHE', SY: 'SYR', TW: 'TWN', TJ: 'TJK', TZ: 'TZA', TH: 'THA', TL: 'TLS', TG: 'TGO', TK: 'TKL', TO: 'TON', TT: 'TTO', TN: 'TUN', TR: 'TUR', TM: 'TKM', TC: 'TCA', TV: 'TUV', UG: 'UGA', UA: 'UKR', AE: 'ARE', GB: 'GBR', US: 'USA', UM: 'UMI', UY: 'URY', UZ: 'UZB', VU: 'VUT', VE: 'VEN', VN: 'VNM', VI: 'VIR', WF: 'WLF', EH: 'ESH', YE: 'YEM', ZM: 'ZMB', ZW: 'ZWE', XK: 'XKX'
};
return iso3166_3[country];
}Install the Integration
Customize the Integration
This is a basic setup. It returns every rate from Postmen and passes default values for the parcel. See the Postmen documentation on rate requests to customize the request further, and the Custom Shipping Code API Reference to output only specific rates or work with them further.
Rates from custom shipping code are not currency converted by Foxy. For non USD stores, set the rate values in the cart's currency yourself. See Shipping Currency Conversion.