| name | carrier-integration |
| description | Builds Karrio shipping carrier integrations from scratch. Covers scaffolding, schema generation, provider implementation (rating, shipping, tracking, pickup, manifest, document upload, address validation), and testing. Triggers on requests to add a new carrier, implement shipping features, build carrier connectors, or integrate carrier APIs with Karrio. |
| disable-model-invocation | true |
Karrio Carrier Integration
Expert guidance for building Karrio shipping carrier integrations. Covers all features, API patterns (JSON/XML/SOAP), and the complete development lifecycle.
Before You Start
- Read CARRIER_INTEGRATION_GUIDE.md for the comprehensive step-by-step guide
- Read CARRIER_INTEGRATION_FAQ.md for common pitfalls and patterns
- Read AGENTS.md for project conventions and coding style
Core Principles
- CLI Tooling is Mandatory: Always use
./bin/cli sdk add-extension for scaffolding. NEVER create files manually.
- Environment First: Run
source ./bin/activate-env before any operation.
- Schema-Driven: Use generated schema types for ALL request/response handling. NEVER use raw dict manipulation.
- Functional Style: Use list comprehensions,
lib.* utilities, and declarative patterns. No imperative loops.
- Pattern Replication: Study existing integrations before implementing.
- Type Safety: Always use
lib.to_object(SchemaType, data) for response parsing.
Reference Carriers
| Pattern | Carrier | Path |
|---|
| JSON API (direct) | SEKO | modules/connectors/seko |
| JSON API (full) | UPS | modules/connectors/ups |
| XML API | Canada Post | modules/connectors/canadapost |
| Hub carrier | Easyship | community/plugins/easyship |
| OAuth flow | FedEx | modules/connectors/fedex |
Integration Workflow
Phase 1: Setup & Scaffolding
source ./bin/activate-env
./bin/cli sdk add-extension \
--path [modules/connectors OR community/plugins] \
--carrier-slug [carrier_slug] \
--display-name "[Carrier Name]" \
--features "rating,shipping,tracking" \
--no-is-xml-api \
--version "2025.5" \
--confirm
Phase 2: Schema Generation
- Add API request/response JSON samples to
schemas/ directory
- Configure
generate script with correct CLI flags:
--nice-property-names for snake_case APIs
--no-nice-property-names for camelCase APIs
--no-append-type-suffix --no-nice-property-names for PascalCase APIs
- Run:
chmod +x [path]/generate && ./bin/run-generate-on [path]
- Verify:
python -c "import karrio.schemas.[carrier] as s; print(dir(s))"
Phase 3: Implementation
Implement files in this order:
- Settings (
karrio/providers/[carrier]/utils.py): Carrier credentials, server URLs, OAuth
- Units (
karrio/providers/[carrier]/units.py): Services, options, tracking status enums
- Error (
karrio/providers/[carrier]/error.py): Error response parser
- Proxy (
karrio/mappers/[carrier]/proxy.py): HTTP communication layer
- Provider functions: Rate, tracking, shipment, etc.
For detailed implementation patterns, see:
Phase 4: Testing
Every feature requires the 4-method test pattern:
class TestCarrierFeature(unittest.TestCase):
def setUp(self):
self.maxDiff = None
self.Request = models.FeatureRequest(**Payload)
def test_create_request(self):
request = gateway.mapper.create_feature_request(self.Request)
print(request.serialize())
self.assertEqual(lib.to_dict(request.serialize()), ExpectedRequest)
def test_api_call(self):
with patch("karrio.mappers.[carrier].proxy.lib.request") as mock:
mock.return_value = "{}"
karrio.Feature.action(self.Request).from_(gateway)
self.assertEqual(mock.call_args[1]["url"], ExpectedURL)
def test_parse_response(self):
with patch("karrio.mappers.[carrier].proxy.lib.request") as mock:
mock.return_value = SuccessResponse
result = karrio.Feature.action(self.Request).from_(gateway).parse()
print(result)
self.assertListEqual(lib.to_dict(result), ExpectedResult)
def test_parse_error(self):
with patch("karrio.mappers.[carrier].proxy.lib.request") as mock:
mock.return_value = ErrorResponse
result = karrio.Feature.action(self.Request).from_(gateway).parse()
print(result)
self.assertListEqual(lib.to_dict(result), ExpectedError)
Phase 5: Validation
python -m unittest discover -v -f [path]/tests
./bin/run-sdk-tests
./bin/cli plugins list | grep [carrier]
./bin/cli plugins show [carrier]
pip install -e [path]
Connector Directory Structure
[carrier]/
āāā pyproject.toml
āāā generate # Schema generation script
āāā schemas/ # JSON/XML API samples
ā āāā rate_request.json
ā āāā rate_response.json
ā āāā ...
āāā karrio/
ā āāā plugins/[carrier]/__init__.py # Plugin METADATA
ā āāā mappers/[carrier]/
ā ā āāā __init__.py # Exports Mapper, Proxy, Settings
ā ā āāā mapper.py # DO NOT MODIFY (auto-generated)
ā ā āāā proxy.py # HTTP communication
ā ā āāā settings.py # Connection settings
ā āāā providers/[carrier]/
ā ā āāā __init__.py # Public exports
ā ā āāā utils.py # Settings + server URLs
ā ā āāā units.py # Enums (services, options)
ā ā āāā error.py # Error parsing
ā ā āāā rate.py # Rating implementation
ā ā āāā tracking.py # Tracking implementation
ā ā āāā shipment/
ā ā āāā create.py # Shipment creation
ā ā āāā cancel.py # Shipment cancellation
ā āāā schemas/[carrier]/ # Generated Python types (DO NOT EDIT)
āāā tests/[carrier]/
āāā fixture.py # Test gateway + mock data
āāā test_rate.py
āāā test_tracking.py
āāā test_shipment.py
Supported Features
| Feature | Request Model | Response Model | Provider File |
|---|
| Rating | RateRequest | RateDetails | rate.py |
| Shipping | ShipmentRequest | ShipmentDetails | shipment/create.py, cancel.py |
| Tracking | TrackingRequest | TrackingDetails | tracking.py |
| Pickup | PickupRequest | PickupDetails | pickup/create.py, update.py, cancel.py |
| Manifest | ManifestRequest | ManifestDetails | manifest.py |
| Document | DocumentUploadRequest | DocumentUploadDetails | document.py |
| Address | AddressValidationRequest | AddressValidationDetails | address.py |
Anti-Patterns
- Manual file creation instead of CLI scaffolding
- Raw dict manipulation instead of generated schema types
- Imperative for-loops instead of list comprehensions
- Modifying
mapper.py (auto-generated, do not touch)
- Editing generated schema files in
karrio/schemas/
- Using pytest instead of unittest
- Bare exception handling
- Reinventing
karrio.lib utilities
- Not using
lib.to_object() for response parsing
- Not using
lib.to_address() for address handling
- Hardcoding service/option codes instead of enums