Documentation Index Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
Use this file to discover all available pages before exploring further.
Shipping Rate Functions
Shipping rate functions allow your app to provide custom shipping options during checkout. Use them to integrate with third-party carriers, offer location-based pricing, or implement complex shipping logic.
How It Works
Function Manifest
Define your shipping rate function in app.json:
{
"handle" : "my-shipping-app" ,
"name" : "Custom Shipping" ,
"version" : "1.0.0" ,
"functions" : {
"shipping_rate" : {
"handle" : "custom-rates" ,
"name" : "Custom Shipping Rates" ,
"config" : {
"apiEndpoint" : "https://my-app.com/api/shipping/rates" ,
"apiKey" : "{{secrets.SHIPPING_API_KEY}}" ,
"defaultCurrency" : "USD"
}
}
}
}
Your function receives cart and address data:
interface ShippingRateInput {
cart : {
items : CartItem [];
subtotal : number ;
totalWeight : number ;
currency : string ;
};
shippingAddress : {
firstName : string ;
lastName : string ;
address1 : string ;
address2 : string ;
city : string ;
province : string ;
provinceCode : string ;
country : string ;
countryCode : string ;
zip : string ;
phone : string ;
};
customer : {
id : string ;
email : string ;
tags : string [];
} | null ;
shop : {
id : string ;
name : string ;
currency : string ;
weightUnit : 'kg' | 'lb' ;
};
}
interface CartItem {
id : string ;
variantId : string ;
productId : string ;
title : string ;
quantity : number ;
price : number ;
weight : number ;
requiresShipping : boolean ;
sku : string ;
properties : Record < string , string >;
}
Output Schema
Return an array of shipping rates:
interface ShippingRateOutput {
rates : ShippingRate [];
}
interface ShippingRate {
// Required fields
name : string ; // Display name (e.g., "Express Shipping")
price : number ; // Price in cents (e.g., 999 = $9.99)
// Optional fields
code ?: string ; // Internal identifier
description ?: string ; // Shown below the rate name
deliveryRange ?: { // Estimated delivery window
min : number ; // Minimum days
max : number ; // Maximum days
};
carrierIdentifier ?: string ; // Carrier name for tracking
phoneRequired ?: boolean ; // Require phone for this rate
}
Example Implementation
Basic Example
// Function config in app.json
{
"functions" : {
"shipping_rate" : {
"handle" : "tiered-shipping" ,
"config" : {
"freeShippingThreshold" : 5000 , // $50.00
"standardRate" : 599 , // $5.99
"expressRate" : 1299 // $12.99
}
}
}
}
// Function logic (executed by backend)
function calculateShippingRates ( input , config ) {
const { cart , shippingAddress } = input ;
const rates = [];
// Check if all items ship free
const allFreeShipping = cart . items . every (
item => item . properties ?. free_shipping === 'true'
);
if ( allFreeShipping || cart . subtotal >= config . freeShippingThreshold ) {
rates . push ({
name: 'Free Shipping' ,
price: 0 ,
description: '5-7 business days' ,
deliveryRange: { min: 5 , max: 7 }
});
} else {
rates . push ({
name: 'Standard Shipping' ,
price: config . standardRate ,
description: '5-7 business days' ,
deliveryRange: { min: 5 , max: 7 }
});
}
// Express shipping (not available for some locations)
const expressExcluded = [ 'HI' , 'AK' , 'PR' ];
if ( ! expressExcluded . includes ( shippingAddress . provinceCode )) {
rates . push ({
name: 'Express Shipping' ,
price: config . expressRate ,
description: '2-3 business days' ,
deliveryRange: { min: 2 , max: 3 },
carrierIdentifier: 'UPS'
});
}
return { rates };
}
Weight-Based Shipping
function calculateWeightBasedRates ( input , config ) {
const { cart , shippingAddress } = input ;
const totalWeight = cart . totalWeight ; // in shop's weight unit
// Weight tiers (weight in kg)
const tiers = [
{ maxWeight: 1 , standard: 499 , express: 999 },
{ maxWeight: 5 , standard: 799 , express: 1499 },
{ maxWeight: 10 , standard: 1299 , express: 2499 },
{ maxWeight: 25 , standard: 1999 , express: 3999 },
{ maxWeight: Infinity , standard: 2999 , express: 5999 }
];
const tier = tiers . find ( t => totalWeight <= t . maxWeight );
return {
rates: [
{
name: 'Standard Shipping' ,
price: tier . standard ,
description: `For orders up to ${ tier . maxWeight } kg` ,
deliveryRange: { min: 5 , max: 7 }
},
{
name: 'Express Shipping' ,
price: tier . express ,
description: `For orders up to ${ tier . maxWeight } kg` ,
deliveryRange: { min: 2 , max: 3 }
}
]
};
}
Zone-Based Shipping
function calculateZoneBasedRates ( input , config ) {
const { shippingAddress , cart } = input ;
// Define shipping zones
const zones = {
domestic: {
countries: [ 'US' ],
rates: { standard: 599 , express: 1299 , overnight: 2499 }
},
canada: {
countries: [ 'CA' ],
rates: { standard: 1299 , express: 2499 }
},
europe: {
countries: [ 'GB' , 'DE' , 'FR' , 'IT' , 'ES' , 'NL' , 'BE' , 'AT' , 'CH' ],
rates: { standard: 1999 , express: 3999 }
},
international: {
countries: [],
rates: { standard: 2999 }
}
};
// Find matching zone
let zone = zones . international ;
for ( const [ name , z ] of Object . entries ( zones )) {
if ( z . countries . includes ( shippingAddress . countryCode )) {
zone = z ;
break ;
}
}
const rates = [];
if ( zone . rates . standard ) {
rates . push ({
name: 'Standard International' ,
price: zone . rates . standard ,
deliveryRange: { min: 7 , max: 14 }
});
}
if ( zone . rates . express ) {
rates . push ({
name: 'Express International' ,
price: zone . rates . express ,
deliveryRange: { min: 3 , max: 5 }
});
}
if ( zone . rates . overnight ) {
rates . push ({
name: 'Overnight' ,
price: zone . rates . overnight ,
deliveryRange: { min: 1 , max: 1 }
});
}
return { rates };
}
Third-Party Carrier Integration
async function fetchCarrierRates ( input , config ) {
const { cart , shippingAddress , shop } = input ;
// Call your carrier API
const response = await fetch ( config . apiEndpoint , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'Authorization' : `Bearer ${ config . apiKey } `
},
body: JSON . stringify ({
origin: shop . address ,
destination: {
city: shippingAddress . city ,
state: shippingAddress . provinceCode ,
zip: shippingAddress . zip ,
country: shippingAddress . countryCode
},
packages: cart . items . map ( item => ({
weight: item . weight * item . quantity ,
quantity: item . quantity
}))
})
});
const carrierRates = await response . json ();
// Transform carrier response to LaunchMyStore format
return {
rates: carrierRates . map ( rate => ({
name: rate . serviceName ,
price: Math . round ( rate . totalPrice * 100 ), // Convert to cents
code: rate . serviceCode ,
description: rate . deliveryEstimate ,
deliveryRange: {
min: rate . transitDays . min ,
max: rate . transitDays . max
},
carrierIdentifier: rate . carrier
}))
};
}
Combining with Store Rates
Your shipping rates appear alongside the merchant’s configured rates:
Shipping Options
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
○ Standard Shipping - $5.99 ← Store rate
5-7 business days
○ Express Shipping - $12.99 ← Store rate
2-3 business days
──────────────────────────────────────────
Powered by My Shipping App
──────────────────────────────────────────
○ Same Day Delivery - $19.99 ← Your function
Today by 8 PM
○ Pickup at Store - FREE ← Your function
Ready in 2 hours
Error Handling
If your function fails, return an error:
function calculateShippingRates ( input , config ) {
const { shippingAddress } = input ;
// Check if we can ship to this address
const unsupportedCountries = [ 'CU' , 'IR' , 'KP' , 'SY' ];
if ( unsupportedCountries . includes ( shippingAddress . countryCode )) {
return {
rates: [],
error: {
code: 'UNSUPPORTED_DESTINATION' ,
message: 'We cannot ship to this destination'
}
};
}
// ... calculate rates
}
If your function returns an error or times out, your app’s shipping rates will not appear. Store-configured rates will still be shown.
Testing
Use the function sandbox to test with various scenarios:
Domestic Order
International Order
Heavy Order
{
"cart" : {
"items" : [
{ "title" : "T-Shirt" , "quantity" : 2 , "price" : 2499 , "weight" : 0.3 }
],
"subtotal" : 4998 ,
"totalWeight" : 0.6
},
"shippingAddress" : {
"city" : "New York" ,
"provinceCode" : "NY" ,
"countryCode" : "US" ,
"zip" : "10001"
}
}
Best Practices
Cache carrier API responses
Carrier APIs can be slow. Cache rates for identical requests to improve checkout speed.
If your carrier API fails, return sensible fallback rates rather than nothing.
Round prices consistently
Always return prices in cents. Round consistently (e.g., always round up for shipping).
Include delivery estimates
Customers want to know when their order will arrive. Always include deliveryRange.
Test with heavy items, multiple items, PO boxes, military addresses, and international destinations.
Offering free shipping above a threshold can increase average order value.
Rate Limits
Shipping rate functions must complete within:
Timeout : 5 seconds
Memory : 128 MB
Functions that exceed limits will fail, and store-configured rates will be used instead.
See Also