Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Integrate a Third-Party Logistics API for Shipping and Tracking in Flu...

Integrate a logistics API in FlutterFlow by connecting to ShipEngine or EasyPost via Cloud Functions. The Cloud Function validates addresses, fetches rate quotes across multiple carriers, creates shipping labels, and returns the label PDF URL and tracking number. FlutterFlow displays the rate comparison and triggers the label purchase when the user selects a shipping option. Store label URLs and tracking numbers in Firestore for later retrieval.

What you'll learn

  • How to validate shipping addresses and get multi-carrier rate quotes via a Cloud Function
  • How to create shipping labels programmatically and store the PDF URL in Firestore
  • How to display a rate comparison ListView with carrier, service, price, and delivery time
  • How to combine label creation with order management using the Firestore orders collection
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate10 min read60-90 minFlutterFlow Free+March 2026RapidDev Engineering Team
TL;DR

Integrate a logistics API in FlutterFlow by connecting to ShipEngine or EasyPost via Cloud Functions. The Cloud Function validates addresses, fetches rate quotes across multiple carriers, creates shipping labels, and returns the label PDF URL and tracking number. FlutterFlow displays the rate comparison and triggers the label purchase when the user selects a shipping option. Store label URLs and tracking numbers in Firestore for later retrieval.

Rate shopping, label generation, and tracking via ShipEngine in one FlutterFlow workflow

A third-party logistics API handles the most complex part of an e-commerce or delivery app: comparing shipping rates across carriers (FedEx, UPS, USPS, DHL) and creating the physical shipping label. Rebuilding carrier API connections individually is months of work. ShipEngine and EasyPost normalize all carrier interactions into a single API, handling rate shopping, label generation, address validation, and tracking in one integration. A Cloud Function proxies these calls (keeping your API keys server-side), and FlutterFlow displays the rate options and handles the user's selection with a purchase action that writes the label data to Firestore.

Prerequisites

  • A FlutterFlow project with Firebase configured (Settings → Project Setup → Firebase)
  • A ShipEngine account with at least one carrier connected (shipengine.com — connect USPS free via Stamps.com)
  • Cloud Functions enabled on Firebase (Blaze plan required)
  • An orders Firestore collection with origin and destination address fields

Step-by-step guide

1

Set up ShipEngine and configure carrier accounts

Create a ShipEngine account at shipengine.com. From the API Keys section, copy your API key. In your Firebase functions directory, set the key in environment variables: firebase functions:config:set shipengine.key='SE-YOUR_KEY'. In the ShipEngine dashboard, connect carrier accounts. ShipEngine provides a built-in test USPS account for development. To add FedEx or UPS, you need existing carrier accounts with those providers — connect them in ShipEngine's Carriers section. Each connected carrier appears automatically in rate quotes. You do not need separate API accounts for each carrier once they are connected to ShipEngine.

Expected result: ShipEngine API key is stored in Cloud Function environment. At least one carrier is connected and available for rate quotes.

2

Create the address validation Cloud Function

Create a Cloud Function named validateAddress. It accepts to_name, to_company, address_line1, address_line2, city_locality, state_province, postal_code, country_code as query or body parameters. Call ShipEngine's address validation endpoint: POST https://api.shipengine.com/v1/addresses/validate with header API-Key. ShipEngine returns status: verified, unverified, or error, plus any corrections to the address fields (normalized street name, city, state). Return the validated address to FlutterFlow. Show any corrections to the user for confirmation: 'We normalized your address to: 123 Main St Apt 4 → 123 MAIN ST STE 4. Is this correct?' This prevents shipping label creation with invalid addresses.

Expected result: Users see a confirmation prompt if their address was normalized, and are blocked from proceeding with unverifiable addresses.

3

Create the rate quote Cloud Function

Create a Cloud Function named getRates. It accepts shipFrom and shipTo address objects, weight (in ounces or grams), dimensions (length, width, height), and a list of carrier IDs. Call ShipEngine POST /v1/rates with the shipment object. ShipEngine returns an array of rate objects across all connected carriers and service levels, each with: carrier_id, carrier_friendly_name, service_code, service_type, shipping_amount.amount (price), delivery_days, estimated_delivery_date, trackable. Filter out rates with error_messages. Sort by shipping_amount ascending. Return the top 5-10 rates. In FlutterFlow, create an API Group pointing to this Cloud Function and display results in a ListView — each rate row shows carrier logo icon, service name, delivery days, and price.

get_rates.js
1const functions = require('firebase-functions');
2const axios = require('axios');
3
4const API_KEY = functions.config().shipengine.key;
5
6exports.getRates = functions.https.onRequest(async (req, res) => {
7 res.set('Access-Control-Allow-Origin', '*');
8 if (req.method === 'OPTIONS') return res.status(204).send('');
9
10 const { shipFrom, shipTo, weight, dimensions } = req.body;
11 if (!shipFrom || !shipTo || !weight) {
12 return res.status(400).json({ error: 'shipFrom, shipTo, and weight required' });
13 }
14
15 try {
16 const { data } = await axios.post(
17 'https://api.shipengine.com/v1/rates',
18 {
19 rate_options: { carrier_ids: [], calculate_tax_amount: false },
20 shipment: {
21 ship_from: shipFrom,
22 ship_to: shipTo,
23 packages: [{ weight: { value: weight, unit: 'ounce' }, dimensions }],
24 },
25 },
26 { headers: { 'API-Key': API_KEY, 'Content-Type': 'application/json' } }
27 );
28
29 const rates = (data.rate_response?.rates || [])
30 .filter((r) => !r.error_messages?.length)
31 .sort((a, b) => a.shipping_amount.amount - b.shipping_amount.amount)
32 .slice(0, 10)
33 .map((r) => ({
34 rateId: r.rate_id,
35 carrier: r.carrier_friendly_name,
36 service: r.service_type,
37 price: r.shipping_amount.amount,
38 currency: r.shipping_amount.currency,
39 deliveryDays: r.delivery_days,
40 estimatedDelivery: r.estimated_delivery_date,
41 }));
42
43 res.json({ rates });
44 } catch (err) {
45 res.status(500).json({ error: err.message });
46 }
47});

Expected result: A ListView shows shipping options from multiple carriers sorted by price. Each row displays carrier name, service level, estimated delivery, and cost.

4

Create the label purchase Cloud Function and store in Firestore

Create a Cloud Function named purchaseLabel. It accepts the rate_id (from the rate quote), shipFrom and shipTo addresses, and orderId (to link the label to the Firestore order). Call ShipEngine POST /v1/labels/rates/{rate_id} to purchase the label for the selected rate. ShipEngine charges the carrier account for the label cost. The response includes label_id, tracking_number, label_download.pdf (URL to the PDF label), label_download.png (URL to PNG preview), and shipment_cost. Write this to Firestore orders/{orderId}: set shippingLabel.trackingNumber, shippingLabel.labelPdfUrl, shippingLabel.carrier, shippingLabel.service, shippingLabel.cost, shippingLabel.purchasedAt. Return the label URL and tracking number to FlutterFlow.

Expected result: The shipping label is purchased from the carrier. The label PDF URL and tracking number are stored on the Firestore order document and returned to the app.

5

Build the complete shipping workflow in FlutterFlow

On your order detail page, add a Create Shipping Label button visible only for orders without a label yet. The button opens a Bottom Sheet with a shipping form: weight TextField, dimensions TextFields (length × width × height), and the destination address (pre-filled from the order if available). Add a Get Rates button that calls the validateAddress function first, then getRates with the form inputs. Bind the results to a ListView of rate options. Each rate row has a Select button. On Select, show a Confirm Shipment dialog showing carrier, service, cost, and estimated delivery. On Confirm, call purchaseLabel. On success, show a Bottom Sheet with the tracking number and a Download Label button that calls Launch URL on the label PDF URL.

Expected result: The complete label workflow is available on the order page: validate address, get rates, select option, confirm and purchase, download label, and retrieve tracking number.

Complete working example

shipping_workflow_schema.txt
1Cloud Functions:
2 validateAddress
3 Input: full address fields
4 Output: { status, normalizedAddress, messages }
5
6 getRates
7 Input: { shipFrom, shipTo, weight, dimensions }
8 Output: { rates: [{ rateId, carrier, service, price, deliveryDays }] }
9
10 purchaseLabel
11 Input: { rateId, shipFrom, shipTo, orderId }
12 Output: { trackingNumber, labelPdfUrl, labelPngUrl, cost }
13
14Firestore: orders/{orderId}
15 ...existing order fields...
16 shippingLabel (Map)
17 trackingNumber: String
18 labelPdfUrl: String
19 labelPngUrl: String
20 carrier: String
21 service: String
22 cost: Double
23 purchasedAt: Timestamp
24
25FlutterFlow Page: OrderDetail
26 Backend Query: orders/{orderId}
27
28 Conditional: if shippingLabel == null
29 Button: 'Create Shipping Label'
30 Open Bottom Sheet: ShippingForm
31
32 Conditional: if shippingLabel != null
33 Text: 'Tracking: ' + trackingNumber
34 Text: 'Carrier: ' + carrier + ' ' + service
35 Text: 'Label cost: $' + cost
36 Button: 'Download Label' Launch URL (labelPdfUrl)
37
38Bottom Sheet: ShippingForm
39 TextField: weight (oz)
40 Row: length × width × height TextFields
41 Button: 'Get Rates' call validateAddress + getRates
42 ListView: rate options
43 Row: carrier + service + '$price' + deliveryDays + Select button
44 Confirm Dialog: carrier + service + cost + delivery date

Common mistakes

Why it's a problem: Creating a shipping label without validating the address first

How to avoid: Always call the validateAddress endpoint before getRates. If the status is not 'verified', show the user the issues or normalized address for confirmation before proceeding. Block label creation for unresolvable addresses.

Why it's a problem: Exposing the ShipEngine API key in FlutterFlow's API Manager headers

How to avoid: All ShipEngine calls must go through Cloud Functions with the key stored in Firebase environment variables. FlutterFlow only calls your Cloud Function URLs, never ShipEngine directly.

Why it's a problem: Not storing the label PDF URL in Firestore after purchase

How to avoid: After purchasing the label, download the PDF and upload it to Firebase Storage via the Cloud Function. Store the permanent Firebase Storage URL in Firestore. The ShipEngine URL should be treated as temporary.

Best practices

  • Use ShipEngine's test mode with test carrier credentials during development to avoid purchasing real labels while building the integration
  • Store the ShipEngine rate_id when displaying rates — it is required to purchase the label and expires after a short window (typically 15-30 minutes), so prompt users to purchase promptly
  • Display the carrier's transit time as 'Estimated delivery: Thu, Apr 3' rather than '3 business days' — specific dates are clearer to customers
  • Implement a void label endpoint to cancel labels that were purchased in error — ShipEngine provides POST /v1/labels/{id}/void within a window before the label is used
  • Log all label purchases to a Firestore shipping_log collection for accounting and dispute resolution
  • Add insurance options to the label purchase form for high-value shipments — ShipEngine supports declared value and carrier insurance
  • Automate label generation for new orders via a Firestore onCreate trigger Cloud Function rather than requiring manual action for high-volume operations

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I am building a FlutterFlow e-commerce app and need to integrate ShipEngine for shipping labels. Write three Firebase Cloud Functions in Node.js: (1) validateAddress that validates a shipping address using ShipEngine's address validation API, (2) getRates that fetches multi-carrier rate quotes for a given shipment (shipFrom, shipTo, weight in ounces, dimensions), and (3) purchaseLabel that buys the label for a selected rate ID, saves the label URL and tracking number to a Firestore orders document, and returns the label PDF URL and tracking number. Include CORS headers and API key authentication via Firebase config.

FlutterFlow Prompt

Add a shipping label workflow to my order detail page in FlutterFlow. Show a 'Create Label' button if no label exists. On tap, open a bottom sheet to collect package weight and dimensions. Call my getRates Cloud Function API and show results in a ListView. When a rate is selected, show a confirmation dialog and call purchaseLabel. On success, show the tracking number and a download button for the label PDF.

Frequently asked questions

Which logistics API should I use for shipping labels in FlutterFlow — ShipEngine or EasyPost?

Both are excellent choices. ShipEngine has a more generous free tier (500 free label purchases per month for USPS) and is easier to set up for beginners. EasyPost has a slightly simpler API and offers better international carrier coverage. Both normalize all carrier interactions into a consistent format. ShipEngine is recommended for new integrations due to its extensive documentation.

Do I need FedEx and UPS accounts to use their shipping rates?

Yes, you need active carrier accounts with FedEx and UPS to use their live rates and purchase labels. Both require business accounts. ShipEngine and EasyPost negotiate discounted rates that are typically better than retail — when you connect your carrier accounts, you get these negotiated rates automatically. USPS can be connected without a separate account via ShipEngine's built-in Stamps.com integration.

How do I print the shipping label from a FlutterFlow app?

ShipEngine returns a label PDF URL. Use Launch URL to open it — most devices will prompt to open in a PDF viewer or browser where it can be printed or shared. For direct printing, use a Custom Action with the printing Flutter package which can print to nearby AirPrint or Google Cloud Print printers directly from the app.

Can I generate international shipping labels for shipments outside the US?

Yes. ShipEngine and EasyPost both support international shipments. For international labels, you also need to provide customs declaration information: commodity description, HS tariff code, declared value, and country of origin. These are required fields for DHL, FedEx International, and UPS International labels.

How do I void a shipping label if it was created by mistake?

ShipEngine provides a void endpoint: POST /v1/labels/{label_id}/void. Call this within the void window (varies by carrier, typically up to 24 hours before the package is scanned). Add a Void Label button to your order detail page visible only for labels less than 24 hours old. Voiding refunds the label cost to your carrier account.

What if I need a fully automated shipping workflow for high-volume orders?

High-volume shipping automation — auto-selecting the cheapest rate per order, bulk label generation, batch printing, carrier pickup scheduling, and customs documentation for international orders — requires significant Cloud Function architecture beyond the basic integration. RapidDev has built production logistics workflows in FlutterFlow for e-commerce businesses processing thousands of shipments per month.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.