End-to-end flows that chain V5 endpoints together. Each workflow lists the exact endpoint sequence and what to do with the response between steps.
Create, rate, label, and ship a single courier package. The most common V5 flow — covers ~80% of carrier integrations.
| # | Step | Endpoint | What you do |
|---|---|---|---|
| 1 | Create the shipment | POST /shipments with type: courier | Send to_address + packages. Returns data.id (the V5 shipment_id). |
| 2 | List rates | GET /rates/{shipment_id} | Pick a carrier.service_code (e.g. canada_post.expedited). |
| 3 | Buy the label | POST /labels/{shipment_id} with the chosen service | Charges credits. Returns data.label_url (PDF) + data.tracking_code. |
| 4 | Schedule a pickup (optional) | POST /pickups with { shipment_ids: [...] } | Only valid for courier shipments (FedEx/UPS/Purolator via Freightcom). |
Use an Idempotency-Key header on steps 1, 3, and 4 so network retries can't double-charge or double-book.
Less-than-truckload freight follows the same shape, just with type: ltl and a different cancel endpoint.
| # | Step | Endpoint | What you do |
|---|---|---|---|
| 1 | Create the LTL shipment | POST /shipments with type: ltl | Returns data.id. |
| 2 | Get LTL quotes | GET /rates/{shipment_id} | LTL carriers (Freightcom). |
| 3 | Book with the carrier | POST /labels/{shipment_id} with the chosen rate | Returns data.confirmation_number. |
| 4 | Cancel a booked LTL (optional) | POST /ltl/{shipment_id}/cancel | Voids the booking with the carrier. Use instead of DELETE /shipments/{id} for LTL. |
Group many shipments into a single batch, generate labels for all of them, and drain per-shipment failures.
| # | Step | Endpoint | What you do |
|---|---|---|---|
| 1 | Create the batch | POST /batches | Send { name, shipment_ids }. Returns data.id. |
| 2 | Trigger processing | POST /batches/{batch_id}/process | Returns 202. Label generation runs async. |
| 3 | Poll status | GET /batches/{batch_id} | Repeat until data.status != "processing". Realistic clients back off (1s → 2s → 4s). |
| 4 | Drain errors | GET /batches/{batch_id}/errors | Per-shipment failures with the validation problem. |
Labels are paid from the account's credit balance. If the balance is low, top up first.
| # | Step | Endpoint | What you do |
|---|---|---|---|
| 1 | Charge the payment method | POST /credits/top-up with { amount, payment_method_id? } | payment_method_id is optional — defaults to the account's default card. Returns data.id (the new transaction). |
| 2 | Confirm the new balance | GET /credits/balance | Sanity-check that the top-up landed and covers the upcoming label cost. |
| 3 | Buy the label | POST /labels/{shipment_id} with the chosen service | Debits the credit balance. |
Always send an Idempotency-Key header on step 1. Top-up retries without a key double-charge.
Customs declarations need a per-product HS code. V5 wraps the TRU classification API so you can ask for a suggested code, optionally verify the manufacturer, then persist the product.
| # | Step | Endpoint | What you do |
|---|---|---|---|
| 1 | Suggest an HS code | POST /products/classify with { title, description } | Returns data.hs_code. |
| 2 | Verify the manufacturer (optional) | POST /products/verify-manufacturer with { manufacturer, country_of_origin } | CUSMA / country-of-origin compliance check. |
| 3 | Create the product | POST /products with the verified hs_code and source fields | Persists the row that store imports and later shipment-creation calls reference. |
If an existing product enters Manufacturer Failed after import, you can run just step 2 on it and re-call POST /products/{sku}/approve-classification.
Some reseller and partner integrations need to show what a shipment would cost at Stallion's standard retail (small business) and enterprise published margins, alongside the account's own quoted rate.
| # | Step | What you do |
|---|---|---|
| 1 | Enable the account setting | Admin enables Show Retail & Enterprise Rates via API (show_tier_rates_api). Uses standard published margins — ignores the account's custom pricing rules. |
| 2 | Quote rates | GET /rates/{shipment_id} or POST /rates as usual. |
| 3 | Read tier_pricing | When enabled, each rate object may include a tier_pricing object with retail and enterprise sub-objects. |
Each tier entry has the same fields as the primary rate (service, subtotal, tax, total, currency, add_ons, etc.), so clients can display them without a separate parser. When the setting is off or the rate cannot be re-margined (no captured postage cost), the tier_pricing key is omitted.
The same opt-in field is available on v4 get-rates responses (rates[].tier_pricing).
Constraints
- Tier totals are derived from the already-fetched carrier cost — no extra carrier API calls.
- Stallion Protection add-ons are recomputed per tier because insurance pricing depends on the rate total.
- Booking always uses the account's primary quoted rate, not a tier comparison.
These don't need a multi-step description — they're single calls or small glue logic.
- Quote rates without creating a shipment —
POST /rateswith shipment details. Returns rates. No shipment row is created. - Validate addresses —
POST /addresses/validatebeforePOST /shipmentsto surface bad zip/postal codes early. - Manage imported orders —
POST /orders,GET /orders,PUT /orders/{order_id}. Delete only before linking to a non-voided shipment. - Find drop-off locations —
GET /locations. Render the returned branch and partner-site addresses; refresh periodically because the catalog changes.
All mutating endpoints (POST, PUT, DELETE) accept an Idempotency-Key header. A retried request with the same key inside 24h returns the original response byte-for-byte. Reusing a key with a different body yields 409 idempotency_conflict. See Errors & Idempotency.