Carrier Integration
Karrio is designed to be easily extensible with new carrier integrations. This guide will walk you through the process of creating a custom carrier integration using Karrioβs built-in CLI tools and following established patterns.
Overview
Integrating a new carrier into Karrio involves several key steps:
- Environment Setup: Activate the Karrio development environment
- Bootstrapping: Use the CLI to generate the extension structure
- Schema Preparation: Create API schema files from carrier documentation
- Schema Generation: Generate Python types from carrier API schemas
- Implementation: Implement the mapping and API communication logic
- Testing: Validate the integration works correctly
Prerequisites
Before you begin, ensure you have:
- A Karrio development environment set up
- Basic Python programming knowledge
- Access to the carrierβs API documentation
- Understanding of the carrierβs authentication and request/response format
Extension Structure
Karrio supports two integration patterns:
Direct Carrier Pattern (Standard)
Located in modules/connectors/[carrier_name]/ for carriers like UPS, FedEx, DHL with direct APIs.
Hub Carrier Pattern (Aggregators)
Located in community/plugins/[carrier_name]/ for multi-carrier services like Easyship, ShipEngine.
Standard Extension Structure:
1modules/connectors/[carrier_name]/ 2βββ pyproject.toml # Package configuration 3βββ setup.py # Legacy setup file 4βββ README.md # Documentation 5βββ generate # Schema generation script 6βββ schemas/ # Raw API schema files 7β βββ error_response.json 8β βββ rate_request.json 9β βββ rate_response.json 10β βββ ... 11βββ karrio/ 12β βββ mappers/ # Integration layer 13β β βββ [carrier_name]/ 14β β βββ __init__.py # Legacy metadata 15β β βββ mapper.py # Main integration class 16β β βββ proxy.py # API client 17β β βββ settings.py # Connection settings 18β βββ providers/ # Implementation logic 19β β βββ [carrier_name]/ 20β β βββ __init__.py 21β β βββ rate.py # Rating implementation 22β β βββ tracking.py # Tracking implementation 23β β βββ shipment/ # Shipping implementation 24β β β βββ __init__.py 25β β β βββ create.py 26β β β βββ cancel.py 27β β βββ pickup/ # Pickup implementation 28β β β βββ __init__.py 29β β β βββ create.py 30β β β βββ update.py 31β β β βββ cancel.py 32β β βββ manifest.py # Manifest implementation 33β β βββ duties.py # Duties calculation 34β β βββ insurance.py # Insurance application 35β β βββ webhook/ # Webhook management 36β β β βββ __init__.py 37β β β βββ register.py 38β β β βββ deregister.py 39β β βββ callback/ # OAuth/webhook callback 40β β β βββ __init__.py 41β β β βββ event.py 42β β β βββ oauth.py 43β β βββ units.py # Enums and constants 44β β βββ utils.py # Utility functions 45β β βββ error.py # Error handling 46β βββ plugins/ # New plugin structure 47β β βββ [carrier_name]/ 48β β βββ __init__.py # Plugin metadata 49β βββ schemas/ # Generated Python types 50β βββ [carrier_name]/ 51β βββ __init__.py 52β βββ rate_request.py 53β βββ ... 54βββ tests/ # Test suite 55 βββ [carrier_name]/ 56 βββ __init__.py 57 βββ fixture.py 58 βββ test_rate.py 59 βββ ...
Step 1: Environment Setup
CRITICAL: Always start by activating the Karrio development environment:
From the project root directory1source ./bin/activate-env
This makes the Karrio CLI (kcli) and development tools available.
Step 2: Bootstrap the Extension
Use the Karrio CLI to generate the complete extension structure:
Interactive mode - CLI will prompt for details1./bin/cli sdk add-extension 2 3# Or with parameters 4./bin/cli sdk add-extension \ 5 --path modules/connectors \ 6 --carrier-slug dhl_express \ 7 --display-name "DHL Express" \ 8 --features "rating,shipping,tracking" \ 9 --is-xml-api false \ 10 --confirm
Available Parameters:
--path:modules/connectors(direct carriers) orcommunity/plugins(hub carriers)--carrier-slug: Unique identifier (e.g.,dhl_express,fedex)--display-name: Human-readable name (e.g., βDHL Expressβ)--features: Comma-separated list of capabilities (see table below)--is-xml-api:truefor XML/SOAP APIs,falsefor JSON APIs
Available Capabilities:
| Capability | Description |
|---|---|
address | Address validation |
callback | OAuth and webhook callback handling |
document | Document upload (customs forms, etc.) |
duties | Duties and taxes calculation |
insurance | Shipment insurance application |
manifest | End-of-day manifest creation |
pickup | Pickup scheduling and management |
rating | Rate/quote retrieval |
shipping | Label creation and shipment management |
tracking | Package tracking |
webhook | Webhook registration/deregistration |
Step 3: Prepare Schema Files
Create JSON (or XML) schema files in the schemas/ directory based on the carrierβs API documentation:
Required Files:
error_response.json(always required)rate_request.json+rate_response.json(if rating enabled)shipment_request.json+shipment_response.json(if shipping enabled)tracking_response.json(if tracking enabled)pickup_request.json+pickup_response.json(if pickup enabled)manifest_request.json+manifest_response.json(if manifest enabled)duties_taxes_request.json+duties_taxes_response.json(if duties enabled)insurance_request.json+insurance_response.json(if insurance enabled)webhook_request.json+webhook_response.json(if webhook enabled)
Note: Use actual API request/response examples, not JSON Schema definitions.
Step 4: Generate Python Types
Generate strongly-typed Python classes from your schema files:
Generate schemas for a specific carrier1./bin/run-generate-on modules/connectors/[carrier_name] 2 3# For hub carriers 4./bin/run-generate-on community/plugins/[carrier_name]
This command:
- Finds and executes the
generatescript in your extension - Uses
kcli codegen generateto convert JSON/XML to Python classes - Creates type-safe classes with
attrsandjstructdecorators
Step 5: Implementation
After generation, implement the integration logic in the created files:
- Settings (
karrio/mappers/[carrier_name]/settings.py): Define connection credentials - Units (
karrio/providers/[carrier_name]/units.py): Define services, options, packaging types - Proxy (
karrio/mappers/[carrier_name]/proxy.py): Implement API communication - Provider Logic (
karrio/providers/[carrier_name]/*.py): Implement request/response mapping
Step 6: Testing & Validation
CRITICAL: Karrio uses a standardized testing framework. Follow these patterns exactly:
Testing Framework Rules
- Use Pythonβs
unittest(never pytest) - Follow exact naming patterns for test files and methods
- Include mandatory debug prints before all assertions
- Use
assertListEqualwith full dict data structures - Mock carrier API responses with actual formats
Test Structure (NEVER DEVIATE)
Every feature requires exactly 4 test methods:
1class Test[CarrierName][Feature](unittest.TestCase): 2 def test_create_[feature]_request(self): # Request transformation 3 def test_[action_verb](self): # HTTP endpoint (mocked) 4 def test_parse_[feature]_response(self): # Success parsing 5 def test_parse_error_response(self): # Error handling
Run Tests
Test specific carrier - MANDATORY BEFORE COMPLETION1python -m unittest discover -v -f modules/connectors/[carrier_name]/tests 2 3# All SDK tests must still pass - MANDATORY 4source ./bin/activate-env && ./bin/run-sdk-tests 5 6# Plugin registration check - MANDATORY 7./bin/cli plugins list | grep [carrier_name]
Test Data Pattern
Each test file must end with exactly these structures:
[Feature]Payload- Karrio input format[Feature]Request- Carrier request format[Feature]Response- Mock carrier responseErrorResponse- Mock error responseParsed[Feature]Response- Expected Karrio output[data, []]ParsedErrorResponse- Expected error format[[], [errors]]
Validation Checklist
π¨ MANDATORY: All criteria must pass before integration is considered complete:
Phase 1: Setup & Generation
- Environment:
source ./bin/activate-envexecuted successfully - CLI Scaffolding: Used
./bin/cli sdk add-extension(never manual creation) - Schema Files: API samples created in
schemas/directory - Schema Generation:
./bin/run-generate-on [path]executed successfully - Generated Types: Python classes exist in
karrio/schemas/[carrier_name]/ - Import Test:
python -c "import karrio.schemas.[carrier_name]"works
Phase 2: Implementation
- Settings: Connection credentials configured properly
- Units: Services, options, packaging types defined
- Proxy: HTTP communication implemented with proper authentication
- Provider Logic: All required features implemented using generated types
- Mapper Untouched:
mapper.pyleft unchanged from CLI generation - Error Handling: Carrier errors parsed to Karrio
Messageobjects
Phase 3: Testing & Registration
- Test Structure: All test files follow exact naming patterns
- Test Methods: Each feature has exactly 4 required test methods
- Carrier Tests:
python -m unittest discover -v -f [path]/testspasses - SDK Tests:
./bin/run-sdk-testsstill passes (no regressions) - Plugin List:
./bin/cli plugins list | grep [carrier_name]shows plugin - Plugin Details:
./bin/cli plugins show [carrier_name]works - Installation:
pip install -e [path]succeeds without errors
Phase 4: Final Validation
- Schema Regeneration:
./bin/run-generate-on [path]still works - Fresh Install: Clean reinstall works after uninstall
- All Features: Every enabled feature (rating, shipping, etc.) tested
- Error Scenarios: Error responses properly handled and tested
Architecture Overview
Next Steps
Follow the detailed guides for specific implementation areas:
- Schema Generation: Generate Python types from API schemas
- Metadata: Configure connection settings and data units
- API Requests: Implement HTTP communication
- Data Mapping: Transform between formats
β οΈ Critical Rules & Common Pitfalls
ABSOLUTE RULES (Violation Will Cause Failure)
-
π¨ NEVER Manual File Creation:
- Always use
./bin/cli sdk add-extensionfor scaffolding - If CLI fails, fix the tool - never create files manually
- Manually created integrations will not work correctly
- Always use
-
π¨ NEVER Edit Generated Files:
- Never modify files in
karrio/schemas/[carrier_name]/ - Never modify
mapper.py(CLI generated, delegation only) - Only edit source schema files and regenerate
- Never modify files in
-
π¨ NEVER Skip Environment Activation:
- Must run
source ./bin/activate-envbefore ANY command - This makes CLI tools available and ensures correct dependencies
- Must run
-
π¨ NEVER Deviate from Test Patterns:
- Test file names, class names, method names are FIXED
- Only adapt data content, never structure or naming
- Use
unittestframework only (never pytest)
Common Mistakes
- Editing mapper.py: This file only contains delegation methods
- Wrong test assertions: Use
assertListEqualwith full dict structures - Missing debug prints: Always add print statements before assertions
- Inconsistent naming: Follow exact patterns from existing integrations
- Manual schema editing: Only modify source files in
schemas/directory
Success Tips
- Study existing integrations like UPS, FedEx for direct carriers
- Study Easyship, ShipEngine for hub carrier patterns
- Always test both success and error scenarios
- Ensure all 6 success criteria pass before considering complete
