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:5002when developing locally, and add thex-test-mode: trueheader 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
/openapion 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
