Connect FlutterFlow to the DHL API using a Firebase Cloud Function proxy that holds your DHL API key and secret, builds the HTTP Basic Auth header server-side, and calls DHL's shipping endpoints. FlutterFlow cannot safely base64-encode and store a key-secret pair client-side — base64 is encoding, not encryption, and it's trivially decoded from a compiled app bundle. The proxy also handles DHL's per-product credentials and per-second rate limits, returning clean rate or tracking JSON to the FlutterFlow app.
| Fact | Value |
|---|---|
| Tool | DHL API |
| Category | Productivity |
| Method | FlutterFlow API Call |
| Difficulty | Advanced |
| Time required | 60 minutes |
| Last updated | July 2026 |
DHL's Basic Auth — What It Is, Why It Can't Live in FlutterFlow
DHL's developer platform uses HTTP Basic Authentication: you combine your API key and secret with a colon (`apikey:apisecret`), base64-encode the result, and send it as `Authorization: Basic <encoded_string>` in every request. The trap that catches many developers: base64 is a reversible encoding, not encryption. If you put the base64 string in a FlutterFlow API Call header, it ships inside the compiled Flutter app bundle. Anyone who downloads your app can extract the app bundle, find the header value, and decode it in seconds using any online base64 decoder. Your DHL account credentials are then exposed.
The solution is the same as for every other secret credential in FlutterFlow: a Firebase Cloud Function that holds the API key and secret as server-side environment variables, builds the `Authorization: Basic` header at request time, calls DHL, and returns only the response data to the app. The app never sees or stores the raw credentials.
DHL's API has another complexity: it's not a single API with one base URL. It's a suite of separate product APIs, each with its own endpoint and potentially its own set of credentials: the DHL Express API for international shipping and rate quotes, the DHL Shipment Tracking API for package tracking, the DHL Location Finder API for service point locations. Your DHL developer account at developer.dhl.com organizes these by 'API Products' — you enable the ones you need and get credentials per product. Your Cloud Function routes to the correct DHL endpoint and credential set based on what the FlutterFlow app is requesting.
DHL also applies per-second rate throttling — burst too many requests and you'll start seeing 429 errors. Implementing exponential backoff in the Cloud Function protects against this during high-traffic periods (like when multiple users check shipping rates simultaneously).
Integration method
FlutterFlow sends parcel dimensions and address data to a Firebase Cloud Function that builds the DHL HTTP Basic Auth header (base64-encoded API key and secret) server-side and calls the appropriate DHL product API. The function returns clean rate quotes or tracking status JSON to FlutterFlow, which binds the data to shipping rate cards or a tracking timeline. The base64 encoding and credentials never exist on the client device.
Prerequisites
- A DHL developer account at developer.dhl.com (free to register)
- DHL API credentials (API key and secret) for the product APIs you need — Tracking, Express, or Location Finder
- A Firebase project with Cloud Functions enabled (Blaze plan required)
- A FlutterFlow project open in your browser
- Basic understanding of FlutterFlow's API Calls panel and Custom Data Types
Step-by-step guide
Step 1: Register on DHL Developer Portal and get API credentials
Go to developer.dhl.com and create a developer account if you don't have one. After registering and verifying your email, log in and navigate to 'My Apps' or the API catalog. Browse the available product APIs and enable the ones your app needs: 'DHL Shipment Tracking' for package tracking, 'DHL Express MyDHL API' for international shipping rates and label creation, or 'DHL Location Finder' for finding DHL service points and drop-off locations. For each API product you enable, DHL provides a separate set of credentials. The free developer tier (sandbox) lets you test with sample tracking numbers and simulated rate quotes without real shipping transactions. For production, you'll need a commercial DHL account to get live credentials. In your DHL developer portal app settings, copy the 'API Key' and 'API Secret' (sometimes labelled 'Consumer Key' and 'Consumer Secret' depending on the product). These are the two values that will be combined and base64-encoded to form the Basic Auth header. Never put these values in FlutterFlow — your next step is placing them in a Firebase Functions config variable. The base URL varies by product: `https://api-eu.dhl.com` is the European endpoint, `https://api.dhl.com` may apply for different regions or products. Always verify the correct base URL in the DHL API documentation for each product you're using — using the wrong base URL returns a 404 even with valid credentials.
Pro tip: Start with the sandbox credentials and sandbox tracking numbers (DHL publishes test tracking numbers in their documentation) before switching to production credentials. This prevents accidental charges and lets you test the full integration safely.
Expected result: You have API Key and API Secret values for each DHL product API you need, ready to set as Firebase Functions config variables.
Step 2: Deploy a Cloud Function that builds Basic Auth and calls DHL
The Cloud Function's core job is to build the `Authorization: Basic` header by combining and base64-encoding your DHL credentials, then forwarding the request to the correct DHL endpoint. This keeps the credentials entirely server-side — the FlutterFlow app sends shipment data but never sees the credentials. Set credentials as Firebase Functions config: run `firebase functions:config:set dhl.api_key="your-key" dhl.api_secret="your-secret"` in the Firebase CLI. For multiple DHL product APIs with separate credentials, use `dhl.tracking_key`, `dhl.tracking_secret`, `dhl.express_key`, `dhl.express_secret`, etc. In the Cloud Function, implement exponential backoff for 429 responses: when DHL returns a rate-limit error, wait a short delay (starting at 500ms, doubling each retry) and retry the request up to 3 times before returning the error to FlutterFlow. This prevents burst failures when multiple users trigger rate lookups simultaneously — a common scenario during checkout flows. For the rate quotes path, the function accepts parcel dimensions and addresses from FlutterFlow in the request body, formats them according to the DHL API's schema (which is more complex than most REST APIs — it uses a structured JSON body with `customerDetails`, `packages`, and `productCode`), calls the DHL Express API, and returns a simplified `rates[]` array with service name, price, and transit time. For tracking, it accepts a tracking number and returns a simplified `events[]` array.
1// Firebase Cloud Function — DHL API proxy with Basic Auth2// functions/index.js3const functions = require('firebase-functions');4const https = require('https');56function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }78async function callDHL(path, method, body, apiKey, apiSecret) {9 const auth = Buffer.from(`${apiKey}:${apiSecret}`).toString('base64');10 const bodyStr = body ? JSON.stringify(body) : null;1112 const options = {13 hostname: 'api-eu.dhl.com',14 path,15 method,16 headers: {17 'Authorization': `Basic ${auth}`,18 'Content-Type': 'application/json',19 ...(bodyStr ? { 'Content-Length': Buffer.byteLength(bodyStr) } : {})20 }21 };2223 let attempt = 0;24 while (attempt < 3) {25 const result = await new Promise((resolve, reject) => {26 const req = https.request(options, (res) => {27 let data = '';28 res.on('data', c => { data += c; });29 res.on('end', () => resolve({ status: res.statusCode, body: JSON.parse(data) }));30 });31 req.on('error', reject);32 if (bodyStr) req.write(bodyStr);33 req.end();34 });35 if (result.status === 429) {36 await sleep(500 * Math.pow(2, attempt));37 attempt++;38 } else {39 return result;40 }41 }42 return { status: 429, body: { error: 'Rate limit exceeded after retries' } };43}4445exports.dhlProxy = functions.https.onRequest(async (req, res) => {46 res.set('Access-Control-Allow-Origin', '*');47 res.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');48 res.set('Access-Control-Allow-Headers', 'Content-Type');49 if (req.method === 'OPTIONS') { res.status(204).send(''); return; }5051 const cfg = functions.config().dhl;52 const action = req.query.action;5354 try {55 if (action === 'track') {56 const trackingNumber = req.query.trackingNumber;57 const result = await callDHL(58 `/track/shipments?trackingNumber=${trackingNumber}`,59 'GET', null, cfg.api_key, cfg.api_secret60 );61 res.status(result.status).json(result.body);62 } else if (action === 'rates') {63 const result = await callDHL(64 '/rate', 'POST', req.body, cfg.api_key, cfg.api_secret65 );66 res.status(result.status).json(result.body);67 } else {68 res.status(400).json({ error: 'Invalid action. Use track or rates.' });69 }70 } catch (err) {71 res.status(500).json({ error: err.message });72 }73});Pro tip: Use `Buffer.from('key:secret').toString('base64')` in Node.js to build the Basic Auth value — this is the correct way to construct it and exactly what you'd do manually. The Cloud Function does this at request time so the base64 string never needs to be stored.
Expected result: A deployed Cloud Function that returns DHL tracking events when called with `?action=track&trackingNumber=123` and rate quotes when POSTed to with `?action=rates` and a parcel data body.
Step 3: Create a FlutterFlow API Group targeting your Cloud Function
In FlutterFlow, click 'API Calls' in the left navigation panel. Click '+ Add' → 'Create API Group'. Name it 'DHL'. Set the Base URL to your Cloud Function's HTTPS trigger URL (e.g., `https://us-central1-your-project.cloudfunctions.net/dhlProxy`). No group-level headers needed — the function handles DHL auth. For tracking: click '+ Add API Call'. Name it 'Track Shipment'. Set Method to GET. In the Variables tab, add a variable `trackingNumber` (String type). In the URL, append `?action=track&trackingNumber={{ trackingNumber }}`. Click 'Response & Test' and paste a sample DHL tracking response. DHL's tracking endpoint returns a `shipments[]` array where each shipment has a `status` object and an `events[]` array. Generate JSON paths for `$.shipments[0].status.statusCode`, `$.shipments[0].status.description`, and `$.shipments[0].events[*].timestamp`, `$.shipments[0].events[*].location.address.addressLocality`, `$.shipments[0].events[*].description`. For rates: add a second API Call named 'Get Rates'. Set Method to POST. In the Body tab, add a JSON body with variables for `weight`, `originCountry`, `originCity`, `destinationCountry`, and `destinationCity`. These become the POST body that the function forwards to DHL's rates endpoint (formatted according to DHL's schema inside the function). Generate JSON paths for the rate options in the response — `$.products[*].productName`, `$.products[*].price[0].price`, `$.products[*].deliveryCapabilities.estimatedDeliveryDateAndTime`.
Pro tip: DHL's rate API has a very specific request body schema. Have the Cloud Function accept simplified inputs (weight, origin, destination as flat fields) and build the full DHL request body inside the function — this keeps the FlutterFlow API Call body simple.
Expected result: Two API Calls (Track Shipment and Get Rates) in the DHL group, both returning 200 with JSON. Tracking events and rate products have bindable JSON paths.
Step 4: Map DHL JSON to Custom Data Types and build the tracking timeline
Go to Settings → Data Types → + Add Data Type. Name it 'TrackingEvent'. Add fields: `timestamp` (String), `location` (String), `description` (String), `statusCode` (String). Create a second Data Type 'ShippingRate' with fields: `productName` (String), `price` (String), `deliveryDate` (String). For the tracking screen, add a Column widget at the top for the current shipment status (large text showing the most recent status description and code). Below, add a ListView for the events timeline. In the ListView's Backend Query, set it to 'API Call' → 'Track Shipment'. In the list item template, create a Row widget with: a colored circle icon on the left (change color conditionally based on the `statusCode` — green for 'delivered', amber for 'transit', grey for older events), and on the right a Column with the event `description` as bold text, the `location` as secondary text, and the `timestamp` formatted to a readable date/time. For conditional icon color: in the circle icon's Color property, add a conditional value — if `statusCode == 'delivered'`, color is green; if `statusCode == 'transit'`, color is amber; else grey. This creates a visual distinction between milestone events. For the rates screen, add a Column with form fields for parcel weight and addresses. Below, add a ListView that executes 'Get Rates' when the user taps a 'Get Quotes' button. Map `productName`, `price`, and `deliveryDate` to Text widgets in each rate card. Add a 'Select' button on each card that stores the chosen rate in App State for use in the checkout flow.
Pro tip: DHL returns timestamps in ISO 8601 format (e.g., '2026-03-15T14:30:00'). Use FlutterFlow's 'Date Time Format' option in the Text widget binding to display them as '15 Mar 2026, 2:30 PM' instead of the raw string.
Expected result: A tracking timeline ListView showing each DHL checkpoint with a colored status icon, event description, location, and timestamp. A rates screen shows DHL shipping options with price and estimated delivery date.
Step 5: Handle label creation and production credential swap
If your app needs to create DHL shipments (not just track or quote), extend the Cloud Function with a label creation path. DHL shipment creation uses the Express API's `POST /expressRate/v1/shipments` endpoint and returns a `shipmentTrackingNumber` and a `label` object containing the base64-encoded PDF label. In the Cloud Function, accept the full shipment details from FlutterFlow, call DHL's creation endpoint, and return the tracking number and a label download URL. For label printing: DHL returns the label as a base64-encoded PDF in the response. The Cloud Function can decode this, upload it to Firebase Storage (using the Admin SDK), and return a public download URL to FlutterFlow. In FlutterFlow, display this URL in a 'Download Label' button that opens the URL in the device browser for printing — or use a WebView if you want in-app preview. For production credential swap: DHL's sandbox credentials (from the developer portal free tier) do not create real shipments or generate real labels. For production, you need a commercial DHL account and live credentials. Update your Firebase Functions config with the production credentials (`firebase functions:config:set dhl.api_key="live-key" dhl.api_secret="live-secret"`) and redeploy. No changes needed in FlutterFlow — the app always calls the Cloud Function URL, and the function uses whichever credentials are configured. If you need DHL credentials, RapidDev's team handles FlutterFlow integrations like this every week, including helping set up commercial DHL accounts — free scoping call at rapidevelopers.com/contact.
Pro tip: Keep separate Firebase Functions config sets for sandbox and production DHL credentials. Use Firebase's multi-environment deployment (staging vs production Firebase projects) to avoid accidentally using live credentials during development.
Expected result: For label creation: the Cloud Function returns a tracking number and a Firebase Storage URL for the PDF label. For production: the same FlutterFlow app works with live DHL credentials after updating the Functions config.
Common use cases
Shipping cost calculator for an e-commerce checkout flow
An e-commerce app where users fill in a delivery address and the app displays DHL shipping rate options before checkout. The app sends parcel weight, dimensions, origin, and destination address to the Cloud Function, which gets DHL Express rate quotes and returns them as a card list — showing service level (Next Day Air, Economy), transit time, and price.
Build a shipping calculator screen that takes parcel weight, dimensions, and destination address as inputs, calls DHL for rate quotes, and displays available shipping options with price and estimated delivery time.
Copy this prompt to try it in FlutterFlow
Package tracking screen with timeline view
An order management app where customers enter a DHL tracking number and see a native timeline of their shipment's journey — from pickup through customs clearance to out-for-delivery status. The app maps DHL's status events to color-coded milestone icons.
Create a package tracking screen where users enter a DHL tracking number and see a vertical timeline of tracking events with status, location, and timestamp for each checkpoint.
Copy this prompt to try it in FlutterFlow
Logistics dashboard showing active shipment statuses
An internal logistics app for a small business owner showing the real-time status of all active DHL shipments — a list of tracking numbers with their current status (In Transit, Out for Delivery, Delivered) and latest location. The list refreshes on pull-to-refresh to show the most current DHL data.
Build a shipment dashboard that shows a list of active DHL tracking numbers with current status, latest location, and estimated delivery date, refreshable by pulling down.
Copy this prompt to try it in FlutterFlow
Troubleshooting
401 Unauthorized from DHL API — 'Authentication failed' or 'Invalid credentials'
Cause: The base64-encoded Basic Auth header is malformed, or the API key/secret values stored in Firebase Functions config contain extra whitespace, line breaks, or are from the wrong DHL product API (e.g., using Tracking credentials to call the Express API).
Solution: Verify the config values with `firebase functions:config:get` — check for any trailing spaces or newlines in the key and secret values. Confirm you're using credentials for the correct DHL product API (Tracking credentials work only for the Tracking API; Express credentials work only for the Express API). Test the base64 encoding manually: `echo -n 'yourkey:yoursecret' | base64` should match what the function is generating.
429 Too Many Requests from DHL during checkout or rate lookup
Cause: DHL applies per-second rate throttling. Multiple simultaneous requests from different app users can hit the limit quickly during peak times. The per-second limit is low enough that a sudden burst — like 10 users all checking rates at the same second — can trigger 429 errors.
Solution: Implement exponential backoff in the Cloud Function as shown in the Step 2 code example — retry with increasing delays (500ms, 1000ms, 2000ms) on 429 responses. For sustained high traffic, contact DHL developer support to increase your rate limit. Also add debouncing in FlutterFlow's rate form — delay the API call by 800ms after the last user input to avoid triggering a rate call on every keystroke.
Tracking returns no events or empty shipments array for a valid DHL tracking number
Cause: The tracking number may be from a different DHL product that uses a different tracking API base URL. DHL Express tracking numbers use the EU tracking endpoint; DHL eCommerce and DHL Parcel use different regional APIs with different endpoints.
Solution: Verify which DHL product issued the tracking number (Express vs eCommerce vs Parcel). Check the DHL developer documentation for the correct endpoint per product. The tracking number format differs by product: Express uses 10-digit numbers, while eCommerce numbers follow a different pattern. Update the Cloud Function to route to the correct tracking API based on the tracking number format or a user-selected DHL product type.
XMLHttpRequest error in FlutterFlow Test Mode — API call blocked in browser
Cause: The Cloud Function is missing CORS headers. FlutterFlow's Test Mode runs in a browser that enforces CORS; native iOS/Android builds are not affected.
Solution: Add `res.set('Access-Control-Allow-Origin', '*')` to the Cloud Function and handle OPTIONS preflight by returning 204. The example code in Step 2 includes this pattern. Redeploy the function after adding CORS headers.
Best practices
- Never put DHL credentials in a FlutterFlow API Call header — base64 encoding is reversible and trivially decoded; keep the API key and secret exclusively in the Cloud Function's environment.
- Handle DHL's per-product credential model in the Cloud Function — Tracking, Express, and Location Finder APIs each have separate credentials; route to the correct one based on the action parameter.
- Implement exponential backoff in the proxy for 429 rate-limit responses — DHL throttles per second, and checkout flows with multiple concurrent users can hit the limit without retry logic.
- Start with DHL's sandbox credentials and published test tracking numbers before switching to production — sandbox mode prevents accidental shipping charges and lets you validate the full integration.
- Validate parcel dimensions and weight in FlutterFlow before sending to the Cloud Function — DHL returns descriptive errors for missing or invalid parcel data, but client-side validation provides a faster user experience.
- Separate the tracking and rates use cases into distinct API Calls with clearly named variables — this makes the FlutterFlow Action Flow Editor easier to navigate and reduces the chance of sending incorrect data to the wrong endpoint.
- For label creation, upload the returned base64 PDF to Firebase Storage in the Cloud Function and return a download URL — displaying a base64-encoded PDF directly in FlutterFlow is not natively supported.
Alternatives
Shippo is a multi-carrier aggregator that includes DHL along with 85+ other carriers in a single unified API with one credential — simpler to integrate than connecting to DHL directly if you need multiple carrier options.
UPS API is a direct alternative for single-carrier shipping if your business uses UPS as its primary carrier — similar Basic Auth model and Cloud Function proxy pattern to DHL.
Eventbrite is unrelated to shipping but shares the same Cloud-Function-as-gateway pattern for proxying a private Bearer token — useful for reference when building the DHL proxy architecture.
Frequently asked questions
Why can't I just put the DHL credentials in a FlutterFlow App Value or App State variable?
App Values and App State are stored client-side — they compile into the Flutter app bundle and exist in device memory during runtime. A basic reverse-engineering tool (like jadx for Android APKs or class-dump for iOS) can extract these values. Base64-decoding the extracted string then reveals your DHL API key and secret in plain text. The only safe storage is a server-side environment variable inside a Cloud Function, where the client never receives the raw credential value.
Does DHL have a free sandbox for testing?
Yes — the DHL developer portal at developer.dhl.com provides sandbox access with test credentials at no cost. The sandbox supports rate quotes with simulated pricing and package tracking with test tracking numbers (published in DHL's API documentation). Production credentials require a commercial DHL shipping account. The same Cloud Function and FlutterFlow integration work for both environments — just update the credentials in Firebase Functions config when switching from sandbox to production.
What if I need to connect to DHL in multiple regions (EU vs US vs Asia)?
DHL has region-specific API base URLs. The EU endpoint is api-eu.dhl.com, and other regions may use different domains — verify in DHL's documentation for each product API you're using. In the Cloud Function, add the region as a parameter accepted from FlutterFlow and route to the appropriate base URL. Alternatively, maintain separate Cloud Functions for each region if the credential sets differ.
Can FlutterFlow display a DHL shipping label PDF to the user?
Not natively — FlutterFlow doesn't have a built-in PDF viewer widget. The recommended approach is: have the Cloud Function upload the base64-decoded PDF to Firebase Storage and return a download URL. In FlutterFlow, add a 'Download Label' button with a 'Launch URL' action pointing to the Firebase Storage URL. This opens the PDF in the device's native PDF viewer or browser. For in-app PDF display, you can use the `flutter_pdfview` pub.dev package in a Custom Widget — but that adds significant complexity compared to the download URL approach.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation