Skip to main content
RapidDev - Software Development Agency
flutterflow-integrationsFlutterFlow API Call

Shippo

Connect FlutterFlow to Shippo using a FlutterFlow API Call to your Cloud Function proxy, which calls the Shippo REST API at https://api.goshippo.com. The critical gotcha: Shippo's auth header is 'Authorization: Shippo <token>' — not 'Bearer'. Using Bearer returns an empty rates array with no error message. Keep the token in a Cloud Function; it can purchase real shipping labels and must never ship inside the app bundle.

What you'll learn

  • Why Shippo's auth header format 'Shippo <token>' is different from standard Bearer tokens and what happens if you get it wrong
  • How to deploy a Firebase Cloud Function that proxies Shippo API calls and keeps the token safely server-side
  • How to create FlutterFlow form fields for parcel dimensions and wire them to the API Call
  • How to map the Shippo rates[] array to rate card widgets using JSON-path binding
  • How to buy a shipping label and open the label URL in a WebView or browser for printing
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate16 min read60 minutesProductivityLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to Shippo using a FlutterFlow API Call to your Cloud Function proxy, which calls the Shippo REST API at https://api.goshippo.com. The critical gotcha: Shippo's auth header is 'Authorization: Shippo <token>' — not 'Bearer'. Using Bearer returns an empty rates array with no error message. Keep the token in a Cloud Function; it can purchase real shipping labels and must never ship inside the app bundle.

Quick facts about this guide
FactValue
ToolShippo
CategoryProductivity
MethodFlutterFlow API Call
DifficultyIntermediate
Time required60 minutes
Last updatedJuly 2026

Shippo in FlutterFlow: The Auth Header That Trips Everyone Up

Shippo is the most popular multi-carrier shipping aggregator among developers building e-commerce and fulfillment apps. One Shippo API account gives you access to rates, labels, and tracking across USPS, UPS, FedEx, DHL, and 80+ other carriers through a single consistent API. For a FlutterFlow app, this means you can add a real shipping rate calculator and label purchase flow without building carrier integrations one by one.

The single most common mistake when integrating Shippo — and the one that generates no useful error message — is using the wrong Authorization header format. Almost every API you have ever worked with uses 'Authorization: Bearer token'. Shippo uses 'Authorization: Shippo token' with a capital S and a single space before the token. If you send 'Bearer token' instead, the API returns HTTP 200 with an empty rates array and no error explaining why. This silent failure is the number-one reason Shippo integrations appear to work (no errors) but return no data. The correct format must be set exactly, and it must be set inside your Cloud Function — not in FlutterFlow.

Shippo uses API token pricing: the API itself is free to integrate, but each label purchased via the API incurs a per-label fee (check current pricing at goshippo.com) plus the actual carrier postage. Shippo provides test tokens (starting with shippo_test_) that let you fetch real rates and simulate label creation without being charged. Use your test token throughout development and switch to your live token only for production. Both tokens follow the same 'Shippo token' auth header format — only the token value differs.

Integration method

FlutterFlow API Call

Shippo exposes a REST API at https://api.goshippo.com for rating shipments, buying labels, and tracking packages across 85+ carriers. In FlutterFlow, you create an API Call Group pointing at your Cloud Function proxy URL. The proxy holds your Shippo API token and makes requests to Shippo using the mandatory 'Authorization: Shippo <token>' header format. FlutterFlow sends parcel form data to the proxy, which returns a rates[] array that you display as rate cards, and optionally calls POST /transactions/ to purchase a label.

Prerequisites

  • A Shippo account — sign up at goshippo.com (free to create; API labels incur per-label fees)
  • Your Shippo test API token (starts with shippo_test_) from the Shippo dashboard under API
  • A Firebase project for deploying the Cloud Function proxy (Blaze plan required for deployment)
  • A FlutterFlow project with a shipping or checkout screen in mind
  • Test address data (origin and destination addresses) for rate testing

Step-by-step guide

1

Get your Shippo API token (test token first)

Log in to your Shippo account at app.goshippo.com. In the left sidebar, click API under the developer settings section. You will see two tokens: a Test Token and a Live Token. The test token starts with `shippo_test_` and allows you to fetch real carrier rates and simulate label purchases without incurring any charges. This is what you should use throughout all development and testing. Copy your test token. Store it securely — you will paste it into your Cloud Function environment variables in the next step, not into any FlutterFlow field. The test token starts with `shippo_test_` followed by a long alphanumeric string. Understand what this token can do: the live token can purchase real shipping labels, which generates real charges to your Shippo account and real postage on real carriers. For this reason, it absolutely must not appear in a FlutterFlow API Call header, an App State variable, or any other location that is compiled into the app bundle. A token embedded in an iOS or Android binary can be extracted by anyone who downloads your app. The Cloud Function proxy pattern is mandatory for any token that can initiate financial transactions. You will return to the Shippo dashboard at the end of development to copy the live token and update your Cloud Function environment variable. Until then, always use the test token.

Pro tip: Shippo test tokens fetch real carrier rate data (actual USPS/UPS/FedEx rates) but don't charge you for label purchases. This means your rate calculator will show real prices even in test mode — which is useful for verifying that rates look correct before going live.

Expected result: You have your Shippo test API token (starting with shippo_test_) copied and ready to use in the Cloud Function setup.

2

Deploy a Firebase Cloud Function proxy with the correct Shippo auth header

Open the Firebase Console at console.firebase.google.com and select your Firebase project. Click Functions in the left sidebar. If you haven't deployed functions before, click Get Started and follow the prompts — you will need to upgrade to the Blaze (pay-as-you-go) plan. The free tier allocation covers development and low-traffic testing at no practical cost. Create a new Cloud Function and name it `shippoProxy`. Set the trigger to HTTP so it gets a public URL. In the function's source code editor, paste the Node.js code shown below. This is the most critical step: pay close attention to line where the Authorization header is set. It MUST be `'Shippo ' + token` — not `'Bearer ' + token`. The 'Shippo' prefix is a capital S followed by exactly one space, followed by the token value. Any deviation from this format — lowercase s, two spaces, adding 'Bearer', or any other variation — causes the Shippo API to return HTTP 200 with an empty `rates` array and no error message. In the Runtime environment variables section of the Cloud Function configuration, add a variable named `SHIPPO_TOKEN` with the value of your test token from Step 1. Deploy the function. Firebase will provide you with an HTTPS URL for the function — copy it for use in FlutterFlow.

index.js
1const functions = require('firebase-functions');
2const fetch = require('node-fetch');
3
4exports.shippoProxy = functions.https.onRequest(async (req, res) => {
5 res.set('Access-Control-Allow-Origin', '*');
6 res.set('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
7 res.set('Access-Control-Allow-Headers', 'Content-Type');
8
9 if (req.method === 'OPTIONS') {
10 res.status(204).send('');
11 return;
12 }
13
14 // CRITICAL: Shippo auth is 'Shippo <token>' — NOT 'Bearer <token>'
15 // Using Bearer returns HTTP 200 with empty rates[] and NO error message.
16 const token = process.env.SHIPPO_TOKEN;
17 const path = req.query.path || 'shipments';
18
19 try {
20 const response = await fetch(`https://api.goshippo.com/${path}`, {
21 method: req.method === 'GET' ? 'GET' : 'POST',
22 headers: {
23 'Content-Type': 'application/json',
24 'Authorization': 'Shippo ' + token // Capital S, one space
25 },
26 body: req.method !== 'GET' ? JSON.stringify(req.body) : undefined
27 });
28
29 const data = await response.json();
30 res.status(response.status).json(data);
31 } catch (err) {
32 res.status(500).json({ error: err.message });
33 }
34});

Pro tip: Test the deployed function before moving to FlutterFlow. Send a POST request to your function URL with a valid shipment body (addresses + parcel) and confirm that the response contains a non-empty rates[] array. If rates[] is empty, check that SHIPPO_TOKEN is set correctly and that the Authorization header says 'Shippo ' not 'Bearer '.

Expected result: The Firebase Cloud Function is deployed and returns a non-empty rates[] array when you test it with a sample shipment body. The function URL is copied and ready for FlutterFlow.

3

Create FlutterFlow form fields for shipment data and an API Call Group

In your FlutterFlow project, navigate to the shipping screen where you want to add the rate calculator. Add the following form fields to collect the data Shippo needs: a TextField for the recipient's name, a TextField for the delivery street address, city, state, and zip code (you can use separate fields or combine them), and TextFields for the parcel weight (in oz or grams), length, width, and height in inches. Add a Button labeled 'Get Rates'. Now click API Calls in the left navigation. Click + Add → Create API Group. Name the group 'Shippo' and set the Base URL to your Cloud Function URL from Step 2 (for example, `https://us-central1-your-project.cloudfunctions.net/shippoProxy`). No Authorization headers are needed in FlutterFlow — the token is handled inside the function. Inside the Shippo API Group, click + Add Call. Name this Call 'GetRates'. Set the method to POST. In the Body tab, set the body type to JSON and paste the shipment request body shown in the code block below. Use `{{ variable }}` syntax for each field that will come from the user's form inputs (recipientName, recipientStreet, recipientCity, recipientState, recipientZip, parcelWeight, parcelLength, parcelWidth, parcelHeight). Switch to the Variables tab and add variables matching each {{ variable }} placeholder in the body. Set sensible test defaults (for example, weight: 16 for a 1-pound parcel). Click Test and fill in a real delivery address — the function should return a rates[] array with multiple carrier options within a second or two.

shipment_body.json
1{
2 "address_from": {
3 "name": "Your Business Name",
4 "street1": "215 Clayton St",
5 "city": "San Francisco",
6 "state": "CA",
7 "zip": "94117",
8 "country": "US"
9 },
10 "address_to": {
11 "name": "{{ recipientName }}",
12 "street1": "{{ recipientStreet }}",
13 "city": "{{ recipientCity }}",
14 "state": "{{ recipientState }}",
15 "zip": "{{ recipientZip }}",
16 "country": "US"
17 },
18 "parcels": [{
19 "length": "{{ parcelLength }}",
20 "width": "{{ parcelWidth }}",
21 "height": "{{ parcelHeight }}",
22 "distance_unit": "in",
23 "weight": "{{ parcelWeight }}",
24 "mass_unit": "oz"
25 }],
26 "async": false
27}

Pro tip: Set 'async': false in the shipment body. This tells Shippo to wait and return all rates in the same response rather than returning a pending status that requires a follow-up polling call.

Expected result: The 'GetRates' API Call tests successfully and returns a JSON response with a rates[] array containing multiple carrier options with prices and estimated transit days.

4

Map rates[] to rate card widgets and wire to the Get Rates button

In your FlutterFlow project, click the Data Types section and add a Custom Data Type named 'ShippoRate'. Add the following fields: `objectId` (String), `provider` (String — the carrier name like 'USPS'), `serviceName` (String — like 'Priority Mail'), `amount` (String — the formatted price), `currency` (String), `estimatedDays` (int or String), and `duration_terms` (String for delivery estimate text). Navigate back to your shipping screen. In the Action Flow Editor, wire the 'Get Rates' button to an action sequence: first call the Shippo → GetRates API Call, passing each form field value to the corresponding variable. After the API Call completes, use an Update App State action to store the rates array in a List-type App State variable using your ShippoRate Custom Data Type. Use these JSON paths to map the Shippo response: `$.rates[*].object_id`, `$.rates[*].provider`, `$.rates[*].servicelevel.name`, `$.rates[*].amount`, `$.rates[*].currency`, and `$.rates[*].estimated_days`. Add a ListView widget below the form fields. Set it to generate children from the App State rates list. Inside the list item template, add a Card with: a Text widget showing `rate.provider + ' ' + rate.serviceName`, a Text widget showing `'$' + rate.amount`, and a Text widget showing `rate.estimatedDays + ' days'`. Add a 'Select' button on each card that stores the selected rate's `objectId` in a separate App State variable. After the user selects a rate, they can tap a 'Buy Label' button that triggers a second API Call — 'CreateTransaction' — that POSTs `{ 'rate': selectedRateId }` to your proxy with path set to 'transactions'. The response contains a `label_url` field that you can open in a WebView or launch in the device browser for printing.

json_paths.txt
1// JSON paths for ShippoRate binding:
2// Rate ID: $.rates[*].object_id
3// Carrier: $.rates[*].provider
4// Service name: $.rates[*].servicelevel.name
5// Price: $.rates[*].amount
6// Currency: $.rates[*].currency
7// Transit days: $.rates[*].estimated_days
8// Delivery terms: $.rates[*].duration_terms
9
10// Transaction body for label purchase:
11// { "rate": "{{ selectedRateId }}", "label_file_type": "PDF", "async": false }
12
13// Label URL JSON path after transaction:
14// $.label_url

Pro tip: Sort the rates list by price (ascending) using a FlutterFlow List Sort action after storing the rates in App State — users always want to see the cheapest option first.

Expected result: The shipping screen shows real carrier rate cards after the user fills in the delivery address and taps 'Get Rates'. Selecting a rate and tapping 'Buy Label' returns a label URL that opens a printable PDF.

5

Swap to the live token for production

When your app is ready for production and you have tested the rate calculator and label purchase flow thoroughly with your `shippo_test_` token, go back to the Shippo dashboard at app.goshippo.com. Under API in the left sidebar, find your Live Token and copy it. In the Firebase Console, navigate to your `shippoProxy` Cloud Function → Runtime environment variables. Update the `SHIPPO_TOKEN` variable from your test token to your live token. Re-deploy the function (Firebase will deploy the updated environment variable without code changes). Verify the live token is working by making one test rate request from your FlutterFlow app — the rates should still appear as before since test and live tokens return the same rate data. However, note that label purchases made with the live token will now generate real charges on your Shippo account and real postage with real tracking numbers. Add a confirmation step in your app before the 'Buy Label' action: show the user the carrier, price, and delivery estimate, and ask them to confirm before the purchase is committed. This prevents accidental label purchases from users who tap the button twice or confirm the wrong rate selection. For tracking existing shipments, add a 'Track' API Call to your Shippo API Group. The endpoint is `GET /tracks/{carrier}/{tracking_number}`. Shippo unifies tracking across all supported carriers — pass the carrier slug (for example, `usps`) and the tracking number as URL path segments, and Shippo returns a unified tracking timeline regardless of which carrier actually shipped the package.

tracking_paths.txt
1// Tracking request (add as a GET call in the Shippo API Group):
2// Endpoint: /tracks/{{ carrier }}/{{ trackingNumber }}
3// Method: GET
4// No body needed
5
6// Tracking JSON paths:
7// Status: $.tracking_status.status
8// Location: $.tracking_status.location.city
9// Status date: $.tracking_status.status_date
10// History entries: $.tracking_history[*].status
11// History locations: $.tracking_history[*].location.city

Pro tip: Keep your test token active in a staging version of your Cloud Function and use the live token only in the production function. This lets you continue testing new features without touching live labels.

Expected result: The live token is active in your Cloud Function. Real label purchases work end-to-end, charge to your Shippo account, and return real tracking numbers. The tracking endpoint returns live tracking data.

Common use cases

E-commerce checkout app with a live shipping rate calculator

A FlutterFlow e-commerce app adds a shipping screen where users enter their delivery address and see real-time shipping rates from multiple carriers. The app calls the Shippo proxy with the parcel weight and dimensions, displays rate cards sorted by price and transit days, and lets the user select a rate to proceed to checkout.

FlutterFlow Prompt

Add a 'Choose Shipping' screen that shows real carrier rates from USPS, UPS, and FedEx for the user's package — display each rate as a card with carrier name, price, and estimated delivery days.

Copy this prompt to try it in FlutterFlow

Fulfillment operations app for warehouse staff

A FlutterFlow app for warehouse workers shows pending orders from a Supabase database and lets staff enter parcel dimensions, fetch Shippo rates, select the cheapest option, and buy a label — all from a mobile device. The label URL returned by Shippo opens in a WebView for printing.

FlutterFlow Prompt

Create a 'Ship Order' screen that takes the order's destination address and box dimensions, shows shipping rates, and has a 'Buy Label' button that opens the label PDF on screen.

Copy this prompt to try it in FlutterFlow

Package tracking screen inside a customer app

A FlutterFlow customer app includes a 'Track Package' screen where users enter a tracking number and see a timeline of tracking events fetched from Shippo's unified tracking API. Shippo returns tracking events from any carrier, so users don't need to know which carrier was used.

FlutterFlow Prompt

Add a 'Track Your Order' screen where users enter their tracking number and see a step-by-step timeline of where their package is — the tracking should work for any carrier.

Copy this prompt to try it in FlutterFlow

Troubleshooting

API Call returns HTTP 200 but rates[] array is empty — no carrier options appear

Cause: The Authorization header is using 'Bearer <token>' instead of the required 'Shippo <token>' format. This is the number-one Shippo integration failure and it produces no error message — just empty data.

Solution: Open your Cloud Function source code and find the Authorization header line. It must read exactly: 'Authorization': 'Shippo ' + token — capital S, one space, then the token. Change 'Bearer' to 'Shippo', re-deploy the function, and re-test. Also verify that SHIPPO_TOKEN is not empty in the function's environment variables.

typescript
1// WRONG — produces empty rates with no error:
2'Authorization': 'Bearer ' + token
3
4// CORRECT:
5'Authorization': 'Shippo ' + token

Getting real label charges during development / accidentally bought real postage

Cause: The live Shippo token was used instead of the test token (shippo_test_...) during development and testing.

Solution: Update your Cloud Function's SHIPPO_TOKEN environment variable to your test token (starts with shippo_test_). Re-deploy. Test tokens fetch real rate data but do not charge for label purchases. Contact Shippo support if you were accidentally charged during testing.

API Call fails in FlutterFlow Test Mode (web) with 'XMLHttpRequest error' or CORS error

Cause: The Cloud Function is not returning CORS headers, or the FlutterFlow API Call is pointed at the Shippo API directly (https://api.goshippo.com) instead of the Cloud Function proxy URL.

Solution: Confirm your FlutterFlow API Group's Base URL points to your Cloud Function URL, not to api.goshippo.com. Verify the Cloud Function includes the CORS headers (Access-Control-Allow-Origin: *) shown in the example code. Re-deploy and re-test.

Rates appear but one or more carriers are missing from the list

Cause: Some carriers require pre-configured carrier accounts in your Shippo dashboard. USPS rates appear automatically, but UPS and FedEx require you to connect your carrier accounts or use Shippo's pre-negotiated rates.

Solution: In the Shippo dashboard, go to Carriers and check which carrier accounts are connected. Click 'Add Carrier' to connect your UPS, FedEx, or DHL account. Alternatively, enable 'Use Shippo's pre-negotiated rates' for carriers you don't have accounts with — this shows discounted rates on those carriers without needing your own account.

Best practices

  • Always set the Authorization header to 'Shippo <token>' — capital S, one space — never 'Bearer <token>'. This is the most common mistake and produces silent failures with empty rates.
  • Use the test token (shippo_test_...) throughout all development and testing. Switch to the live token only in your production Cloud Function, never in development.
  • Never put the Shippo API token in a FlutterFlow API Call header — it can purchase real shipping labels and incur real charges. Keep it exclusively in a Cloud Function environment variable.
  • Set async: false in your shipment POST body so Shippo returns all rates in a single synchronous response rather than requiring you to poll for a pending result.
  • Add a purchase confirmation dialog before the 'Buy Label' action that shows the carrier, service, price, and estimated delivery date — prevent accidental label purchases from double-taps.
  • Sort rate cards by price ascending after binding the rates[] array so users always see the cheapest option first without manually scanning through carrier options.
  • Include empty rates handling in your UI: show a 'No rates available' message if the rates[] array is empty, with a prompt to check the delivery address format and parcel dimensions.
  • If you build a RapidDev scoping call at rapidevelopers.com/contact — their team handles FlutterFlow shipping integrations every week and can save you the debugging time on the auth header and label flow.

Alternatives

Frequently asked questions

Why does Shippo use 'Shippo token' instead of 'Bearer token' in the auth header?

Shippo uses a custom authentication scheme rather than the standard OAuth Bearer format. The header must be exactly 'Authorization: Shippo <your-token>' with a capital S and one space. This is documented in Shippo's API reference, but it is easy to miss if you're used to Bearer-based APIs. The consequence of using Bearer is a silent failure — HTTP 200 with an empty rates array and no error message — which makes it particularly difficult to diagnose.

Can I show Shippo rates without a backend proxy?

Not safely. The Shippo API token can purchase real shipping labels that incur real charges. If the token is embedded in your FlutterFlow app bundle, anyone who decompiles the app can extract it and use it to buy postage at your expense. The Cloud Function proxy is the correct approach for any token with purchase scope. For purely read-only tracking lookups using a public tracking number, the security risk is lower, but a proxy is still recommended practice.

Will Shippo rates in test mode match real carrier rates?

Yes. Shippo test tokens fetch real carrier rate data from USPS, UPS, FedEx, and other connected carriers — they just don't charge you for label purchases. This means the rates you see in development are the actual rates your users will see in production, which is very useful for testing your rate card UI with realistic data.

Can FlutterFlow receive Shippo webhooks for tracking events?

Not directly. FlutterFlow compiles to a client app and cannot receive inbound HTTP webhook calls. To receive Shippo tracking webhooks, you need a Firebase Cloud Function with a webhook endpoint URL. The function receives the tracking event from Shippo, writes the update to Firestore or Supabase, and your FlutterFlow app reads the updated status from the database using Firestore's real-time listeners or Supabase Realtime.

How do I show the purchased label in the app?

After a successful POST to /transactions/, the Shippo response includes a label_url field containing a direct link to a PDF label. In FlutterFlow, you can open this URL using the Launch URL action (opens in the device browser or a PDF viewer) or embed it in a WebView widget on a dedicated 'Label' screen. The label PDF is typically accessible for 24-48 hours after creation — download and store it in Supabase Storage or Firebase Storage if you need long-term access.

RapidDev

Talk to an Expert

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

Book a free consultation

Integrations are where projects stall

We wire FlutterFlow integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

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.