There are two completely different things in PrestaShop that both get called "setting up a carrier," and conflating them is where most merchants get stuck. The first is creating a carrier by hand in the back office — a name, some zones, a price grid — which is plumbing PrestaShop has always shipped with. The second is installing a carrier module: a piece of software that registers one or more carriers for you, talks to a real courier's API, prints labels, pulls live rates, and drops pickup-point maps into your checkout. This guide is about the second one — what a carrier module actually is, how it hooks into PrestaShop, how to install and configure it without breaking checkout, and how to tell a good one from a liability. For building carriers manually from zones and ranges, that's a different job and we cover it in the complete shipping configuration guide.
What a carrier module actually is (and why it's different from a back-office carrier)
When you go to Shipping → Carriers and click "Add new carrier," you create a row in the ps_carrier table that PrestaShop evaluates against the cart at checkout. It's static: the price comes from a grid you typed in, and the carrier knows nothing about the courier beyond a name and a tracking-URL template. That's fine for "Standard Shipping €5.99," and it's the right tool for a lot of stores.
A carrier module is a different animal. On install, it programmatically creates its own carrier(s) — it instantiates the Carrier class in its install() method, sets id_reference, ranges, zones and groups in code, and stores the resulting carrier ID in its own configuration so it can manage that carrier across upgrades. From then on the module owns that carrier. So what does that buy you? Behaviour the static grid can't do:
- Live rates. Instead of a price you guessed, the module calls the courier's API at checkout and returns the real quote for this cart's weight, dimensions and destination — by implementing
getOrderShippingCostExternal()on a carrier flagged as a module carrier withneed_rangeoff, so PrestaShop asks the module for the price instead of reading a static range grid. - Pickup points. Relay-point and locker carriers inject a map or dropdown into the delivery step (via the displayCarrierExtraContent hook, the 1.7+ replacement for the deprecated
displayCarrierList) so the customer chooses a specific locker, and the module saves that choice against the order. The hook only fires for the carrier whoseexternal_module_namepoints at the module. - Labels and manifests. A back-office button (or an order-status hook) generates the courier's shipping label PDF and writes the tracking number straight onto the order — no copy-pasting into the courier's own portal.
- Self-healing config. Because the carrier is created in code, the module can recreate or update it after a PrestaShop upgrade instead of you rebuilding a price grid by hand.
The practical takeaway: if your courier offers an official PrestaShop module, you almost always want it over a hand-built carrier — the manual version can't print a label or quote a real price. If your courier doesn't, a manual carrier is your fallback, and the configuration guide above is where to start.
Before you install anything: three checks that prevent broken checkouts
Carrier modules touch the most fragile page in your store, so the order of operations matters. Store owners' single biggest fear is a module that breaks checkout — earn that trust by checking three things first.
- Compatibility with your exact version. The carrier API landscape changed across PrestaShop 1.6 → 1.7 → 8 → 9; a module written for 1.6 may register its carrier in a way 8.x ignores. Confirm the module lists your version before you buy, not after.
- A staging copy. Never install a carrier module first on production. Clone the shop, install there, and run a real test order to each zone. A carrier that fails to register correctly shows up as the dreaded "no carriers available" at checkout — and you do not want to discover that with live customers.
- A database backup. Because these modules write to
ps_carrier,ps_carrier_zoneand the range tables on install, a clean restore point means a bad install is a five-minute rollback rather than an afternoon.
The install-and-configure sequence that actually works
Most official carrier modules follow the same shape. Knowing the sequence means you can configure any of them — DHL, GLS, Colissimo, InPost — without re-learning the workflow each time.
- 1. Install the module. Modules → Module Manager → Upload a module, drop the ZIP, install. At this moment the module creates its carrier(s) — go to Shipping → Carriers and you'll see them appear automatically.
- 2. Enter your account credentials. Open the module's configuration and paste the API key / client ID / contract number the courier gave you. This is what separates a working live-rate carrier from a dead one. Most modules have a "test connection" button — use it; a green tick here saves you a failed checkout later.
- 3. Map zones and services. The module created a carrier, but you still decide where it ships and which of the courier's services (standard, express, locker) you expose. Assign the carrier to your zones under its Shipping → Carriers entry, or inside the module if it manages zones itself.
- 4. Set the fallback price. Live-rate modules need a fallback for when the courier API times out — otherwise an API hiccup becomes a lost order. Set a sensible flat price the module falls back to so checkout never shows zero carriers.
- 5. Configure handling and label options. Sender address, default package weight, label format (A4 vs thermal), and which order status triggers label generation.
- 6. Place a real test order through to payment for each zone, confirm the carrier appears, the rate looks right, and (if supported) a label generates.
Manual carrier vs carrier module — which to use
| Hand-built back-office carrier | Carrier module | |
|---|---|---|
| Setup | You type a name, zones, price grid | Install ZIP; carrier appears automatically |
| Pricing | Static grid you maintain by hand | Can pull live rates from the courier API |
| Pickup points / lockers | Not possible | Map or dropdown injected into checkout |
| Labels & tracking | Manual — copy into the courier portal | One-click label PDF; tracking written to the order |
| Maintenance | You re-edit prices when the courier changes rates | Live rates update themselves; flat grids still need edits |
| Best when… | Simple flat/threshold shipping, or no official module exists | Your courier offers a module and you want real rates, lockers or labels |
Picking real-courier modules: the integrations that exist
Once you've decided you want a module, the question is which one — and that's courier-specific, because each integration has its own quirks (contract numbers, locker APIs, label formats). Rather than cram them into one rushed list, here's the map to the carrier-by-carrier guides:
- The big international three. DHL, DPD and UPS cover most cross-border EU shipping; their setup, contract fields and label flow are walked through in the DHL, DPD and UPS integration guide.
- GLS across multiple countries. If you ship to several markets on one GLS contract, the multi-country configuration has its own gotchas — see GLS multi-country shipping integration.
- France: Colissimo and Mondial Relay. Colissimo is the national-carrier default for French stores (Colissimo setup guide); Mondial Relay adds the relay-point network French shoppers expect (Mondial Relay relay-point delivery).
- Poland: InPost Paczkomaty. If you sell into Poland and don't offer locker delivery, you're at a real disadvantage — the locker-map integration is covered in InPost Paczkomaty for PrestaShop.
The "no carriers available" problem — and what causes it with modules
The most common failure after installing a carrier module is checkout showing no shipping options at all. With a hand-built carrier this is almost always a missing zone assignment; with a module there are a few extra suspects, because the module added a layer:
- The module's carrier isn't assigned to the customer's zone. The module created the carrier but you never mapped your destination countries to it. Fix under Shipping → Carriers → the carrier's Shipping locations and costs.
- No price range covers this cart. Some modules still rely on a weight/price range row for availability or as a fallback, so if the API returns nothing and no range covers the cart, the carrier can vanish. A true external-rate carrier with
need_rangeoff won't depend on a static range row at all — but if the module left a range requirement in place, that's a suspect. - API credentials are wrong or the courier API is down. Without a fallback price, a failed API call silently drops the carrier. This is exactly why step 4 above matters.
- Group or shop restriction. The module may default its carrier to one customer group or one shop in a multistore. Check Group access and Shop association on the carrier.
- Product has no weight. Weight-based and live-rate carriers both lean on real product weights in the catalogue; depending on the module and range setup, a missing or 0 kg weight can break weight-based or API rating — or fall outside every configured range — so the carrier mis-prices or disappears.
For the deeper logic of how zones, ranges and carriers fit together — the model these errors all trace back to — the complete shipping configuration guide is the reference.
Carrier modules are half the job — the customer-facing half still matters
A perfectly installed carrier module quotes the right price and prints the label, but the customer experience around it is a separate decision you shouldn't skip:
- What delivery date do they see? PrestaShop's built-in transit-time field shows the same "3–7 business days" to every destination regardless of zone or your handling time. Showing a real date instead is its own topic — see setting realistic delivery expectations and why a concrete date reduces customer anxiety. Our Estimated Delivery Date module fills that gap: it calculates an actual arrival date per carrier and zone, factoring your handling time and business days, so a German customer sees "Delivery: Wednesday" while a Spanish customer sees "Delivery: Friday" — without you editing a theme file.
- Do they get told once it ships? The tracking number your module wrote onto the order is only useful if it reaches the customer — covered in tracking numbers and delivery notifications.
- What do you charge on top of the courier's rate? Whether you pass live rates straight through, flatten them, or absorb them into free shipping is a margin decision — start with free vs flat vs calculated and, if you're a smaller store, shipping strategy for small stores.
The short version
"Setting up a carrier module" isn't the same as adding a carrier in the back office — a module installs software that owns its own carrier, talks to the courier, and does the things a static price grid never could: live rates, locker maps, one-click labels. Install on staging with a backup, enter your contract credentials and test the connection, always set a fallback price so an API blip can't empty your checkout, and place a real test order to every zone before you go live. Then pick the right courier integration for your markets, and make sure the customer-facing side — delivery dates, tracking, what you charge — is as considered as the plumbing behind it. Get those right and the carrier step stops being a source of abandoned carts and starts being one less reason to leave.
Comments
No comments yet. Be the first!
Be the first to ask a question or share useful feedback.
Leave a comment
Share a question, an installation detail, or feedback that could help another reader.