Carrier for USPS
Carrier for USPS
USPS for Carrier, on the USPS APIs v3
(apis.usps.com), not the retired Web Tools XML. Prices, domestic, international and return
labels, voids, reprints, tracking, carrier pickups, Hold for Pickup at a Post Office, and US address
validation.
This package translates one API and nothing else. Packing, checkout, label storage, the claim that stops a label being bought twice, tracking schedules, the point picker and the log are Carrier's.
Requirements
- Craft CMS 5.3+, Craft Commerce 5, PHP 8.2+
- Carrier 5.0+
- A USPS developer-portal app (Consumer Key and Secret). Labels, international labels and carrier pickup are separate access requests on the portal.
- For labels: a USPS business account with a CRID, a MID and a funded EPS (or permit) account, from the Business Customer Gateway.
Installation
composer require justinholtweb/craft-carrier-usps
php craft plugin/install carrier-usps
Then add a USPS connection under Carrier → Connections and press Test. The test reads which APIs your app has been granted and, when the payment account is filled in, proves it can pay for a label.
What it does
| Feature | Notes |
|---|---|
| Live rates | Shipping Options prices every class in one call, once per distinct package |
| Labels | PDF 4×6, ZPL (203 dpi) and PNG; one call per package (USPS has no shipment grouping) |
| Multi-piece | One tracking number per package, in order; see "Partial failure" below |
| International | First-Class Package, Priority Mail and Priority Mail Express International, with customs |
| Returns | Pay-on-use return labels (/labels/v3/return-label) |
| Void | Cancelled at no charge, or a refund request when USPS has already processed the label |
| Reprint | Domestic and international |
| Tracking | Tracking 3.2 batch, 35 numbers a request |
| Post Offices | Search-mode points, for Hold for Pickup services |
| Carrier pickup | Free, Monday to Saturday |
| Address validation | US addresses and territories |
Things worth knowing
- Quota. USPS's default is 60 calls per hour per API. Keep Carrier's rate cache on.
- Dimensions are required for both prices and labels.
- Payment token. Labels need a second token (~8 h) minted from your CRID, MID and EPS account. It is cached per connection and per account, so changing the account never pays with the old one.
- Hold for Pickup needs the customer's email and phone, and a first and last name, email and phone on the ship-from address.
- International labels are one package per shipment, because each carries its own customs form.
- TEM (the sandbox) labels are watermarked and not billed.
Partial failure
USPS buys labels one package at a time. If package 2 fails after package 1 was bought:
- a refusal or throttle voids package 1 at once. Only if USPS confirms the void cost nothing is the shipment reported as refused, so it can be retried;
- anything else — no answer for package 2, a refund dispute instead of a clean void, a void that fails — marks the shipment uncertain, with every tracking number already bought in the message. Carrier never buys an uncertain shipment again on its own.
Unverified
Written against the USPS OpenAPI specs and examples without a live account. These are marked
UNVERIFIED in src/carriers/UspsCarrier.php, and should be confirmed in TEM:
- the machinable thresholds that choose
NONSTANDARD(from the DMM, not the API spec); - Hold for Pickup on Priority Mail and Priority Mail Express (USPS's example only shows Ground Advantage), and which street address the label should carry;
- the account fields on the
LABEL_OWNERpayment role; - Pub 199 tracking event codes and
statusCategorytext; - the meaning of every
DPVConfirmationletter; - the 60-calls-per-hour default quota.
Testing
docker exec -w /var/www/html ddev-plugin-testing-web php /var/www/craft-carrier/tests/integration/carriers.php usps
tests/fixtures.php answers every endpoint with USPS's documented shapes so the conformance
suite runs the happy path.
Licence
The Craft License. See LICENSE.md. Carrier for USPS is free: no editions, no licence key, and no licensing code in the plugin. It needs Carrier, which is commercial.
To install this plugin, copy the command above to your terminal.
This plugin doesn't have any reviews.






