📖 Looking for karrio's legacy docs? Visit docs.karrio.io

Shipping Integration Guide

Modern fulfillment platforms need to support multiple carriers without implementing dozens of carrier-specific APIs. Karrio provides a unified REST interface that normalizes carrier interactions into a consistent workflow: authenticate, connect carriers, fetch rates, purchase labels, track packages, and receive real-time updates.

This guide walks through the complete integration lifecycle using real API examples and production-ready patterns. Each section includes the JSON payloads you’ll work with and highlights the fields that matter for building reliable shipping workflows.

[!TIP] Everything below works in both test mode and live mode. Point your requests at http://localhost:5002 when developing locally, and add the x-test-mode: true header to interact with carrier sandboxes.

Architecture Overview

Karrio sits between your fulfillment system and carrier networks, handling the complexity of multi-carrier integration while you maintain a single API contract.

The platform handles authentication, rate shopping across carriers, label generation, document storage, and event distribution. Your application makes REST calls and receives webhooks—no need to maintain carrier-specific SDKs or handle individual API quirks.

Authentication & Authorization

Karrio supports two authentication mechanisms designed for different integration patterns. API keys are recommended for server-to-server communication, while JWT tokens work well for client-side applications or temporary access scenarios.

API Key Authentication

Server-side integrations should use API keys. Include your key in the Authorization header for every request:

1Authorization: Token <YOUR_API_KEY>

API keys are permanent credentials that can be revoked or rotated through the dashboard. They carry the same permissions as the user who created them.

JWT Token Authentication

For client-side applications or temporary access, use JWT tokens. First, obtain a token pair:

1curl -X POST "$KARRIO_API_URL/api/token" \ 2 -H "Content-Type: application/json" \ 3 -d '{ 4 "email": "admin@example.com", 5 "password": "demo" 6 }'
1{ 2 "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...", 3 "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..." 4}

Access tokens expire after a set period. Use the refresh token to obtain new access tokens without re-authenticating. For subsequent requests, use Bearer authentication:

1Authorization: Bearer <ACCESS_TOKEN>

Carrier Connection Workflow

Before you can fetch rates or purchase labels, you need to connect at least one carrier account. Karrio maintains these connections and uses them to route shipping requests.

Use Case: Multi-Region Fulfillment

Consider an e-commerce platform with warehouses in North America and Europe. Each region uses different carriers with separate accounts. By connecting multiple carriers and tagging them with metadata, you can route shipments based on origin, destination, or business rules.

Endpoint: POST /v1/connections

1curl -X POST "$KARRIO_API_URL/v1/connections" \ 2 -H "Authorization: Token $KARRIO_API_KEY" \ 3 -H "Content-Type: application/json" \ 4 -d '{ 5 "carrier_name": "fedex", 6 "carrier_id": "fedex_us_west", 7 "credentials": { 8 "api_key": "YOUR_API_KEY", 9 "secret_key": "YOUR_SECRET_KEY", 10 "account_number": "YOUR_ACCOUNT_NUMBER" 11 }, 12 "test_mode": false, 13 "metadata": { 14 "region": "us-west", 15 "fulfillment_center": "LAX" 16 } 17 }'
1{ 2 "id": "car_1a2b3c4d5e6f", 3 "carrier_id": "fedex_us_west", 4 "carrier_name": "fedex", 5 "display_name": "FedEx", 6 "test_mode": false, 7 "active": true, 8 "capabilities": ["rating", "shipping", "tracking", "manifest"], 9 "metadata": { 10 "region": "us-west", 11 "fulfillment_center": "LAX" 12 }, 13 "created_at": "2024-01-15T10:00:00Z" 14}

The capabilities array tells you what operations this carrier supports. Not all carriers support all features—some may only offer tracking, while others provide full shipping and manifesting capabilities. The carrier_id becomes your reference for this specific connection in subsequent API calls.

Connection Flow

Rate Shopping Experience

Rate shopping is typically the first interaction customers have with shipping costs. The Proxy API provides stateless rate requests—no shipment records are created, making it ideal for checkout flows where you need quotes without commitment.

Use Case: Real-Time Checkout Quotes

A customer adds items to their cart and proceeds to checkout. Your application sends their address and parcel details to Karrio, which queries all connected carriers in parallel and returns normalized rates within seconds.

Endpoint: POST /v1/proxy/rates

1curl -X POST "$KARRIO_API_URL/v1/proxy/rates" \ 2 -H "Authorization: Token $KARRIO_API_KEY" \ 3 -H "Content-Type: application/json" \ 4 -d '{ 5 "shipper": { 6 "postal_code": "V6M2V9", 7 "city": "Vancouver", 8 "country_code": "CA", 9 "state_code": "BC", 10 "address_line1": "5840 Oak St", 11 "person_name": "Jane Doe", 12 "company_name": "Sender Corp" 13 }, 14 "recipient": { 15 "postal_code": "E1C4Z8", 16 "city": "Moncton", 17 "country_code": "CA", 18 "state_code": "NB", 19 "address_line1": "125 Church St", 20 "person_name": "John Smith", 21 "company_name": "Receiver Inc" 22 }, 23 "parcels": [ 24 { 25 "weight": 1.0, 26 "weight_unit": "KG", 27 "package_preset": "canadapost_corrugated_small_box" 28 } 29 ], 30 "options": { 31 "currency": "CAD", 32 "insurance": 100.00, 33 "signature_confirmation": true 34 }, 35 "carrier_ids": ["canadapost"] 36 }'
1{ 2 "rates": [ 3 { 4 "id": "rat_a1b2c3d4e5f6", 5 "object_type": "rate", 6 "carrier_name": "canadapost", 7 "carrier_id": "canadapost", 8 "currency": "CAD", 9 "service": "canadapost_priority", 10 "total_charge": 106.71, 11 "transit_days": 2, 12 "estimated_delivery": "2024-01-20", 13 "extra_charges": [ 14 { "name": "Base charge", "amount": 101.83, "currency": "CAD" }, 15 { "name": "Fuel surcharge", "amount": 2.7, "currency": "CAD" }, 16 { "name": "SMB Savings", "amount": -11.74, "currency": "CAD" }, 17 { "name": "Discount", "amount": -9.04, "currency": "CAD" }, 18 { "name": "Duties and taxes", "amount": 13.92, "currency": "CAD" } 19 ], 20 "meta": { 21 "service_name": "CANADAPOST PRIORITY", 22 "rate_provider": "canadapost", 23 "carrier_connection_id": "car_1a2b3c4d5e6f" 24 }, 25 "test_mode": true 26 }, 27 { 28 "id": "rat_b2c3d4e5f6g7", 29 "object_type": "rate", 30 "carrier_name": "canadapost", 31 "carrier_id": "canadapost", 32 "currency": "CAD", 33 "service": "canadapost_expedited_parcel", 34 "total_charge": 45.20, 35 "transit_days": 5, 36 "estimated_delivery": "2024-01-23", 37 "extra_charges": [ 38 { "name": "Base charge", "amount": 42.50, "currency": "CAD" }, 39 { "name": "Fuel surcharge", "amount": 2.70, "currency": "CAD" } 40 ], 41 "meta": { 42 "service_name": "CANADAPOST EXPEDITED PARCEL", 43 "rate_provider": "canadapost" 44 }, 45 "test_mode": true 46 } 47 ], 48 "messages": [] 49}

The response includes everything needed to present shipping options to customers. The service field is the key identifier you’ll use when purchasing labels. The extra_charges array provides transparency into carrier fees, surcharges, and discounts—useful for displaying itemized breakdowns or debugging rate discrepancies.

Rate Request Options

Karrio supports a wide range of shipping options that modify pricing and service availability:

  • insurance: Shipment insurance value
  • signature_confirmation: Require signature on delivery
  • saturday_delivery: Enable Saturday delivery (carrier-specific)
  • dangerous_good: Mark as hazardous material
  • hold_at_location: Hold at carrier facility for pickup
  • currency: Preferred currency for rate display
  • declared_value: Customs value for international shipments

Label Purchase Workflows

Label purchase is the core transaction in shipping. Karrio supports two distinct workflows: two-step purchases that save rates for later selection, and single-call purchases that complete transactions immediately.

Two-Step Purchase Flow

This workflow is ideal when you need to present multiple shipping options to customers and let them choose before committing to a purchase.

Step 1: Create Shipment and Fetch Rates

Endpoint: POST /v1/shipments

1curl -X POST "$KARRIO_API_URL/v1/shipments" \ 2 -H "Authorization: Token $KARRIO_API_KEY" \ 3 -H "Content-Type: application/json" \ 4 -d '{ 5 "shipper": { 6 "postal_code": "V6M2V9", 7 "city": "Vancouver", 8 "country_code": "CA", 9 "state_code": "BC", 10 "address_line1": "5840 Oak St", 11 "person_name": "Jane Doe", 12 "company_name": "Sender Corp", 13 "phone_number": "514-000-9999" 14 }, 15 "recipient": { 16 "postal_code": "E1C4Z8", 17 "city": "Moncton", 18 "country_code": "CA", 19 "state_code": "NB", 20 "address_line1": "125 Church St", 21 "person_name": "John Smith", 22 "company_name": "Receiver Inc", 23 "phone_number": "514-000-0000" 24 }, 25 "parcels": [ 26 { 27 "weight": 1.0, 28 "weight_unit": "KG", 29 "package_preset": "canadapost_corrugated_small_box" 30 } 31 ], 32 "payment": { 33 "paid_by": "sender", 34 "currency": "CAD" 35 }, 36 "carrier_ids": ["canadapost"] 37 }'
1{ 2 "id": "ship_a1b2c3d4e5f6", 3 "object_type": "shipment", 4 "status": "draft", 5 "carrier_name": null, 6 "tracking_number": null, 7 "label_url": null, 8 "rates": [ 9 { 10 "id": "rat_a1b2c3d4e5f6", 11 "service": "canadapost_priority", 12 "total_charge": 106.71, 13 "currency": "CAD", 14 "transit_days": 2 15 } 16 ], 17 "shipper": { "...": "..." }, 18 "recipient": { "...": "..." }, 19 "parcels": [ { "...": "..." } ], 20 "test_mode": true, 21 "created_at": "2024-01-15T10:00:00Z" 22}

The shipment is created in draft status with available rates attached. Present these rates to your customer, collect their selection, and proceed to purchase.

Step 2: Purchase Selected Rate

Endpoint: POST /v1/shipments/{id}/purchase

1curl -X POST "$KARRIO_API_URL/v1/shipments/ship_a1b2c3d4e5f6/purchase" \ 2 -H "Authorization: Token $KARRIO_API_KEY" \ 3 -H "Content-Type: application/json" \ 4 -d '{ 5 "selected_rate_id": "rat_a1b2c3d4e5f6" 6 }'
1{ 2 "id": "ship_a1b2c3d4e5f6", 3 "object_type": "shipment", 4 "status": "purchased", 5 "carrier_name": "canadapost", 6 "carrier_id": "canadapost", 7 "tracking_number": "123456789012", 8 "shipment_identifier": "123456789012", 9 "tracking_url": "/v1/trackers/canadapost/123456789012", 10 "label_url": "/v1/shipments/ship_a1b2c3d4e5f6/label.pdf", 11 "invoice_url": null, 12 "label_type": "PDF", 13 "service": "canadapost_priority", 14 "selected_rate": { 15 "id": "rat_a1b2c3d4e5f6", 16 "service": "canadapost_priority", 17 "total_charge": 106.71, 18 "currency": "CAD", 19 "carrier_name": "canadapost" 20 }, 21 "shipping_documents": [ 22 { 23 "category": "label", 24 "format": "PDF", 25 "url": "/v1/shipments/ship_a1b2c3d4e5f6/label.pdf", 26 "base64": "JVBERi0xLjQKJeLjz9MKNSAwIG9iago8PC9..." 27 } 28 ], 29 "meta": { 30 "service_name": "CANADAPOST PRIORITY", 31 "rate_provider": "canadapost" 32 }, 33 "test_mode": true, 34 "created_at": "2024-01-15T10:00:00Z" 35}

The shipment transitions to purchased status. The shipping_documents array contains the label with base64-encoded content, allowing immediate rendering without a second API call. You now have a tracking_number for monitoring and a label_url for document retrieval.

Single-Call Purchase Flow

For automated workflows or when the service selection is predetermined, you can purchase labels in a single API call by specifying the service directly.

Endpoint: POST /v1/shipments

1curl -X POST "$KARRIO_API_URL/v1/shipments" \ 2 -H "Authorization: Token $KARRIO_API_KEY" \ 3 -H "Content-Type: application/json" \ 4 -d '{ 5 "shipper": { "...": "..." }, 6 "recipient": { "...": "..." }, 7 "parcels": [ { "...": "..." } ], 8 "service": "canadapost_priority", 9 "label_type": "PDF", 10 "reference": "ORDER-5002", 11 "carrier_ids": ["canadapost"] 12 }'
1{ 2 "id": "ship_b2c3d4e5f6g7", 3 "status": "purchased", 4 "carrier_name": "canadapost", 5 "tracking_number": "123456789012", 6 "label_url": "/v1/shipments/ship_b2c3d4e5f6g7/label.pdf", 7 "invoice_url": null, 8 "service": "canadapost_priority", 9 "reference": "ORDER-5002", 10 "shipping_documents": [ 11 { 12 "category": "label", 13 "format": "PDF", 14 "url": "/v1/shipments/ship_b2c3d4e5f6g7/label.pdf", 15 "base64": "JVBERi0xLjQKJeLjz9MKNSAwIG9iago8PC9..." 16 } 17 ] 18}

Karrio fetches rates internally, selects the matching service, and purchases the label in one transaction. The response includes shipping_documents with the base64-encoded label for immediate use. This reduces round trips and simplifies automation for high-volume workflows.

Purchase Flow Comparison

Document Management

Shipping labels and commercial invoices are critical documents in shipping workflows. Karrio provides two access patterns: immediate base64 access during label purchase, and URL-based retrieval for subsequent access.

Shipping Documents Access Patterns

When you purchase a label (via POST /v1/shipments with a service or POST /v1/shipments/{id}/purchase), the response includes a shipping_documents array with base64-encoded content:

1{ 2 "shipping_documents": [ 3 { 4 "category": "label", 5 "format": "PDF", 6 "url": "/v1/shipments/ship_xxxxx/label.pdf", 7 "base64": "JVBERi0xLjQKJeLjz9MKNSAwIG9iago8PC9..." 8 }, 9 { 10 "category": "invoice", 11 "format": "PDF", 12 "url": "/v1/shipments/ship_xxxxx/invoice.pdf", 13 "base64": "JVBERi0xLjQKMCAwIG9iag..." 14 } 15 ] 16}

This design enables:

  • Immediate rendering: Print labels directly from the purchase response without additional API calls
  • Reduced latency: No need to wait for a second request to download the document
  • Simplified workflows: Handle label creation and display in a single operation

For subsequent retrievals (list shipments, get shipment), base64 is null to reduce payload size:

1{ 2 "shipping_documents": [ 3 { 4 "category": "label", 5 "format": "PDF", 6 "url": "/v1/shipments/ship_xxxxx/label.pdf", 7 "base64": null 8 } 9 ] 10}

Use the url field to download documents when base64 is not available.

Token-Based Document Access

For secure document retrieval, Karrio provides a token-based access system that generates short-lived URLs.

Use Case: Batch Label Printing

A warehouse needs to print 50 labels every morning. Instead of making 50 separate authenticated requests, they request a single batch token that grants access to all labels for 5 minutes—enough time to download and print everything.

Document Access Flow

Request Resource Token

Endpoint: POST /api/tokens

1curl -X POST "$KARRIO_API_URL/api/tokens" \ 2 -H "Authorization: Token $KARRIO_API_KEY" \ 3 -H "Content-Type: application/json" \ 4 -d '{ 5 "resource_type": "shipment", 6 "resource_ids": ["ship_a1b2c3d4e5f6"], 7 "access": ["label"], 8 "format": "pdf", 9 "expires_in": 300 10 }'
1{ 2 "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", 3 "expires_at": "2024-01-15T10:05:00Z", 4 "resource_urls": { 5 "ship_a1b2c3d4e5f6": "/v1/shipments/ship_a1b2c3d4e5f6/label.pdf?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." 6 } 7}

The resource_urls map provides ready-to-use download URLs for each requested resource. These URLs can be shared with warehouse systems, embedded in emails, or used for direct browser downloads—all without exposing your API key.

Download Document

1curl -X GET "$KARRIO_API_URL/v1/shipments/ship_a1b2c3d4e5f6/label.pdf?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \ 2 --output label.pdf

Supported Access Types

  • label: Shipping label (PDF, PNG, ZPL, GIF)
  • invoice: Commercial invoice for international shipments
  • manifest: End-of-day manifest for carrier pickup
  • batch_labels: Multiple labels combined into one document
  • batch_invoices: Multiple invoices combined into one document

Order Management Integration

Karrio provides an optional order management layer for platforms that want to sync orders from external systems and track fulfillment status.

Use Case: Shopify to Karrio Sync

An e-commerce store receives orders through Shopify. A webhook triggers when orders are placed, sending order data to Karrio for storage. Warehouse staff then creates shipments directly from these orders, and Karrio updates the order status as fulfillment progresses.

Endpoint: POST /v1/orders

1curl -X POST "$KARRIO_API_URL/v1/orders" \ 2 -H "Authorization: Token $KARRIO_API_KEY" \ 3 -H "Content-Type: application/json" \ 4 -d '{ 5 "order_id": "ORDER-5002", 6 "order_date": "2024-01-15", 7 "source": "Shopify", 8 "shipping_to": { 9 "person_name": "John Smith", 10 "address_line1": "125 Church St", 11 "city": "Moncton", 12 "country_code": "CA", 13 "postal_code": "E1C4Z8" 14 }, 15 "line_items": [ 16 { 17 "sku": "ITEM-001", 18 "title": "Widget", 19 "quantity": 2, 20 "weight": 0.75, 21 "weight_unit": "KG", 22 "value_amount": 25.00, 23 "value_currency": "CAD" 24 } 25 ], 26 "metadata": { 27 "customer_id": "12345", 28 "sales_channel": "web" 29 } 30 }'
1{ 2 "id": "ord_a1b2c3d4e5f6", 3 "object_type": "order", 4 "order_id": "ORDER-5002", 5 "order_date": "2024-01-15", 6 "source": "Shopify", 7 "status": "unfulfilled", 8 "shipping_to": { "...": "..." }, 9 "line_items": [ { "...": "..." } ], 10 "shipments": [], 11 "metadata": { "...": "..." }, 12 "test_mode": true, 13 "created_at": "2024-01-15T10:00:00Z" 14}

Orders remain in unfulfilled status until shipments are created and purchased. The shipments array populates as labels are purchased, providing a complete audit trail of fulfillment activity.

Tracking and Event Distribution

Tracking provides visibility into package movement from pickup to delivery. Karrio polls carrier tracking APIs and normalizes events into a consistent format across all carriers.

Tracking Data Flow

Retrieve Tracking Details

Endpoint: GET /v1/trackers/{carrier_name}/{tracking_number}

1curl -X GET "$KARRIO_API_URL/v1/trackers/ups/1Z12345E6205277936" \ 2 -H "Authorization: Token $KARRIO_API_KEY"
1{ 2 "id": "trk_a1b2c3d4e5f6", 3 "object_type": "tracker", 4 "carrier_name": "ups", 5 "carrier_id": "ups_package", 6 "tracking_number": "1Z12345E6205277936", 7 "status": "in_transit", 8 "delivered": false, 9 "estimated_delivery": "2024-01-20", 10 "events": [ 11 { 12 "code": "KB", 13 "date": "2024-01-15", 14 "time": "10:39", 15 "description": "Package received by carrier", 16 "location": "BONN, DE" 17 }, 18 { 19 "code": "IT", 20 "date": "2024-01-16", 21 "time": "08:15", 22 "description": "In transit", 23 "location": "FRANKFURT, DE" 24 } 25 ], 26 "info": { 27 "carrier_tracking_link": "https://www.ups.com/track?tracknum=1Z12345E6205277936", 28 "package_weight": 1.5, 29 "package_weight_unit": "KG", 30 "shipping_date": "2024-01-15" 31 }, 32 "test_mode": true 33}

Events are sorted chronologically with the most recent first. The status field provides a high-level state (in_transit, delivered, out_for_delivery, delivery_failed), while individual events give detailed scan history.

Webhook Configuration

Real-time updates arrive via webhooks. Register an endpoint to receive notifications when tracking status changes, labels are purchased, or orders are fulfilled.

Configure webhooks through the dashboard at Developers > Webhooks or use the API. Subscribe to specific event types to filter what notifications you receive.

Example Webhook Payload:

1{ 2 "event": "tracker.updated", 3 "data": { 4 "id": "trk_a1b2c3d4e5f6", 5 "tracking_number": "123456789012", 6 "carrier_name": "canadapost", 7 "status": "delivered", 8 "delivered": true, 9 "events": [ 10 { 11 "code": "DEL", 12 "description": "Delivered", 13 "date": "2024-01-20", 14 "time": "14:30", 15 "location": "Moncton, NB" 16 } 17 ] 18 }, 19 "pending_webhooks": 0, 20 "test_mode": true 21}

Respond with 200 OK immediately to acknowledge receipt. Process the payload asynchronously in a background worker to avoid blocking the webhook delivery. Store event.id to detect and ignore duplicate deliveries.

Advanced Configuration

Carrier-Specific Options

Different carriers support unique features beyond the standard options. Consult the API reference for carrier-specific parameters.

FedEx Freight Example:

1{ 2 "options": { 3 "fedex_freight_class": "CLASS_50", 4 "fedex_smart_post_hub_id": "5531", 5 "fedex_smart_post_indicia": "PARCEL_SELECT" 6 } 7}

UPS Saturday Delivery:

1{ 2 "options": { 3 "ups_saturday_delivery_indicator": true, 4 "ups_shipper_release": true 5 } 6}

International Shipments

Cross-border shipments require customs declarations. Include commodity details, HS codes, and duty payment information to ensure smooth customs clearance.

1{ 2 "customs": { 3 "content_type": "merchandise", 4 "incoterm": "DDU", 5 "invoice": "INV-001", 6 "invoice_date": "2024-01-15", 7 "commodities": [ 8 { 9 "description": "Widget", 10 "quantity": 2, 11 "weight": 0.75, 12 "weight_unit": "KG", 13 "value_amount": 25.00, 14 "value_currency": "CAD", 15 "hs_code": "8471.30.01", 16 "origin_country": "CA" 17 } 18 ], 19 "duty": { 20 "paid_by": "sender", 21 "account_number": "123456" 22 } 23 } 24}

Production Considerations

Error Handling

Carrier APIs can be unpredictable. Implement retry logic with exponential backoff for network failures, and handle carrier-specific error codes gracefully. The messages array in responses contains warnings and errors from carriers.

Rate Caching

Cache rate responses for 5-15 minutes to improve checkout performance while maintaining pricing accuracy. Rates can change frequently due to fuel surcharges and dimensional weight calculations, so balance performance with freshness.

Address Validation

Validate addresses before rating to prevent carrier rejections and unexpected surcharges. Invalid addresses lead to failed shipments and additional fees.

Async Processing

Offload label generation and webhook processing to background queues. This keeps your application responsive and prevents timeouts during high-volume periods.

Test Mode

Thoroughly test integrations in test mode before connecting production carrier credentials. Test mode uses carrier sandboxes, allowing you to verify workflows without incurring charges.

Reference Materials

  • API Reference: Navigate to /openapi on your instance for interactive API documentation
  • Carrier Connections: Review supported carriers at /v1/references
  • Local Development: Follow the Setup API in a local instance guide
  • Custom Carriers: Learn about Carrier Integration for adding bespoke carriers