CargoWise eAdapter Integration Patterns (2025 Complete Guide)

Jan 19, 2025
cargowiseeadapterintegrationfreight-forwarding
0

CargoWise eAdapter is the cornerstone of freight forwarding software integration, enabling seamless data exchange between CargoWise and external systems. Understanding eAdapter integration patterns is crucial for building robust, scalable integrations that can handle the complex workflows of modern logistics operations.

This comprehensive guide covers everything you need to know about CargoWise eAdapter integration, from basic message types to advanced error handling patterns. Whether you're a developer building your first CargoWise integration or an experienced architect designing enterprise-scale solutions, this guide will provide the technical foundation you need.

Understanding CargoWise eAdapter Architecture

What is eAdapter?

CargoWise eAdapter is a message-based integration framework that enables external systems to communicate with CargoWise through XML message exchanges. Unlike REST APIs, eAdapter uses a more traditional EAI (Enterprise Application Integration) approach with message queues and asynchronous processing.

Key Characteristics:

  • Message-Based: Uses XML messages for data exchange
  • Asynchronous: Supports both synchronous and asynchronous processing
  • Reliable: Built-in retry mechanisms and error handling
  • Scalable: Can handle high-volume message processing
  • Flexible: Supports various message types and custom extensions

eAdapter Message Flow

The eAdapter integration follows a standard request-response pattern:

  1. External System sends XML message to eAdapter
  2. eAdapter validates and processes the message
  3. CargoWise processes the business logic
  4. eAdapter returns response message to external system
  5. Error Handling manages failures and retries

Core Message Types

Universal Shipment Message

The Universal Shipment message is the most commonly used eAdapter message type, handling shipment data across different modes of transport.

Message Structure:

<?xml version="1.0" encoding="UTF-8"?>
<UniversalShipment>
  <Header>
    <MessageId>MSG-2025-001</MessageId>
    <Timestamp>2025-01-19T10:00:00Z</Timestamp>
    <SourceSystem>ExternalSystem</SourceSystem>
  </Header>
  <Shipment>
    <ShipmentId>SH-2025-001</ShipmentId>
    <Mode>Air</Mode>
    <Origin>
      <City>New York</City>
      <Country>US</Country>
      <Airport>JFK</Airport>
    </Origin>
    <Destination>
      <City>London</City>
      <Country>GB</Country>
      <Airport>LHR</Airport>
    </Destination>
    <Cargo>
      <Description>Electronics</Description>
      <Weight>150.5</Weight>
      <Volume>2.3</Volume>
      <Pieces>5</Pieces>
    </Cargo>
  </Shipment>
</UniversalShipment>

Implementation Example (C#):

public class UniversalShipmentMessage
{
    public string MessageId { get; set; }
    public DateTime Timestamp { get; set; }
    public string SourceSystem { get; set; }
    public ShipmentData Shipment { get; set; }
}

public class ShipmentData
{
    public string ShipmentId { get; set; }
    public string Mode { get; set; }
    public LocationData Origin { get; set; }
    public LocationData Destination { get; set; }
    public CargoData Cargo { get; set; }
}

public async Task<string> SendUniversalShipment(UniversalShipmentMessage message)
{
    var xml = SerializeToXml(message);
    var response = await eAdapterClient.SendMessageAsync(xml);
    return response;
}

Consol Message

Consol messages handle consolidation data for groupage shipments and LCL (Less than Container Load) operations.

Message Structure:

<?xml version="1.0" encoding="UTF-8"?>
<Consol>
  <Header>
    <MessageId>CON-2025-001</MessageId>
    <Timestamp>2025-01-19T10:00:00Z</Timestamp>
  </Header>
  <ConsolData>
    <ConsolId>CON-2025-001</ConsolId>
    <MasterAWB>123-45678901</MasterAWB>
    <HouseAWBs>
      <AWB>123-45678902</AWB>
      <AWB>123-45678903</AWB>
      <AWB>123-45678904</AWB>
    </HouseAWBs>
    <Consolidator>
      <Name>ABC Freight Forwarders</Name>
      <Code>ABC</Code>
    </Consolidator>
  </ConsolData>
</Consol>

Forward Message

Forward messages handle forwarding instructions and routing information.

Message Structure:

<?xml version="1.0" encoding="UTF-8"?>
<Forward>
  <Header>
    <MessageId>FWD-2025-001</MessageId>
    <Timestamp>2025-01-19T10:00:00Z</Timestamp>
  </Header>
  <ForwardData>
    <ForwardId>FWD-2025-001</ForwardId>
    <ShipmentId>SH-2025-001</ShipmentId>
    <Instructions>
      <Text>Deliver to warehouse 3, dock 2</Text>
      <Priority>High</Priority>
    </Instructions>
    <Routing>
      <NextStop>Warehouse 3</NextStop>
      <Contact>John Smith</Contact>
      <Phone>+1-555-0123</Phone>
    </Routing>
  </ForwardData>
</Forward>

Error Handling Patterns

Standard Error Response Structure

eAdapter returns standardized error responses that include detailed error information:

<?xml version="1.0" encoding="UTF-8"?>
<ErrorResponse>
  <Header>
    <MessageId>ERR-2025-001</MessageId>
    <Timestamp>2025-01-19T10:00:00Z</Timestamp>
    <Status>Error</Status>
  </Header>
  <Error>
    <Code>VALIDATION_ERROR</Code>
    <Message>Invalid shipment data provided</Message>
    <Details>
      <Field>ShipmentId</Field>
      <Issue>Required field is missing</Issue>
    </Details>
  </Error>
</ErrorResponse>

Error Handling Implementation

C# Error Handling:

public class eAdapterErrorHandler
{
    public async Task<eAdapterResponse> HandleResponse(string xmlResponse)
    {
        var response = DeserializeXml<eAdapterResponse>(xmlResponse);
        
        if (response.Header.Status == "Error")
        {
            var error = response.Error;
            
            switch (error.Code)
            {
                case "VALIDATION_ERROR":
                    return await HandleValidationError(error);
                case "SYSTEM_ERROR":
                    return await HandleSystemError(error);
                case "TIMEOUT_ERROR":
                    return await HandleTimeoutError(error);
                default:
                    return await HandleGenericError(error);
            }
        }
        
        return response;
    }
    
    private async Task<eAdapterResponse> HandleValidationError(ErrorData error)
    {
        // Log validation error
        _logger.LogError($"Validation error: {error.Message}");
        
        // Return appropriate response
        return new eAdapterResponse
        {
            Status = "ValidationError",
            Message = error.Message,
            ShouldRetry = false
        };
    }
    
    private async Task<eAdapterResponse> HandleSystemError(ErrorData error)
    {
        // Log system error
        _logger.LogError($"System error: {error.Message}");
        
        // Return retry response
        return new eAdapterResponse
        {
            Status = "SystemError",
            Message = error.Message,
            ShouldRetry = true,
            RetryAfter = TimeSpan.FromMinutes(5)
        };
    }
}

Python Error Handling:

import logging
from typing import Dict, Any, Optional
from dataclasses import dataclass
from enum import Enum

class ErrorCode(Enum):
    VALIDATION_ERROR = "VALIDATION_ERROR"
    SYSTEM_ERROR = "SYSTEM_ERROR"
    TIMEOUT_ERROR = "TIMEOUT_ERROR"
    NETWORK_ERROR = "NETWORK_ERROR"

@dataclass
class eAdapterError:
    code: ErrorCode
    message: str
    details: Optional[Dict[str, Any]] = None

class eAdapterErrorHandler:
    def __init__(self, logger: logging.Logger):
        self.logger = logger
    
    def handle_error(self, error: eAdapterError) -> Dict[str, Any]:
        """Handle eAdapter errors with appropriate response"""
        
        if error.code == ErrorCode.VALIDATION_ERROR:
            self.logger.error(f"Validation error: {error.message}")
            return {
                "status": "error",
                "retry": False,
                "message": error.message
            }
        
        elif error.code == ErrorCode.SYSTEM_ERROR:
            self.logger.error(f"System error: {error.message}")
            return {
                "status": "error",
                "retry": True,
                "retry_after": 300,  # 5 minutes
                "message": error.message
            }
        
        elif error.code == ErrorCode.TIMEOUT_ERROR:
            self.logger.warning(f"Timeout error: {error.message}")
            return {
                "status": "error",
                "retry": True,
                "retry_after": 60,  # 1 minute
                "message": error.message
            }
        
        else:
            self.logger.error(f"Unknown error: {error.message}")
            return {
                "status": "error",
                "retry": False,
                "message": "Unknown error occurred"
            }

Retry Logic and Resilience Patterns

Exponential Backoff Implementation

C# Retry Pattern:

public class eAdapterRetryHandler
{
    private readonly int _maxRetries;
    private readonly TimeSpan _baseDelay;
    private readonly double _backoffMultiplier;
    
    public eAdapterRetryHandler(int maxRetries = 3, TimeSpan baseDelay = default, double backoffMultiplier = 2.0)
    {
        _maxRetries = maxRetries;
        _baseDelay = baseDelay == default ? TimeSpan.FromSeconds(1) : baseDelay;
        _backoffMultiplier = backoffMultiplier;
    }
    
    public async Task<T> ExecuteWithRetry<T>(Func<Task<T>> operation, Func<Exception, bool> shouldRetry = null)
    {
        var retryCount = 0;
        var delay = _baseDelay;
        
        while (retryCount < _maxRetries)
        {
            try
            {
                return await operation();
            }
            catch (Exception ex) when (shouldRetry?.Invoke(ex) ?? true)
            {
                retryCount++;
                
                if (retryCount >= _maxRetries)
                {
                    throw new eAdapterMaxRetriesExceededException($"Operation failed after {_maxRetries} retries", ex);
                }
                
                _logger.LogWarning($"Operation failed (attempt {retryCount}/{_maxRetries}): {ex.Message}. Retrying in {delay.TotalSeconds} seconds.");
                
                await Task.Delay(delay);
                delay = TimeSpan.FromMilliseconds(delay.TotalMilliseconds * _backoffMultiplier);
            }
        }
        
        throw new InvalidOperationException("Retry logic should not reach this point");
    }
}

Python Retry Pattern:

import asyncio
import random
from typing import Callable, Any, Optional
from functools import wraps

class eAdapterRetryHandler:
    def __init__(self, max_retries: int = 3, base_delay: float = 1.0, backoff_multiplier: float = 2.0):
        self.max_retries = max_retries
        self.base_delay = base_delay
        self.backoff_multiplier = backoff_multiplier
    
    def retry(self, should_retry: Optional[Callable[[Exception], bool]] = None):
        def decorator(func: Callable) -> Callable:
            @wraps(func)
            async def wrapper(*args, **kwargs) -> Any:
                retry_count = 0
                delay = self.base_delay
                
                while retry_count < self.max_retries:
                    try:
                        return await func(*args, **kwargs)
                    except Exception as ex:
                        if should_retry and not should_retry(ex):
                            raise
                        
                        retry_count += 1
                        
                        if retry_count >= self.max_retries:
                            raise eAdapterMaxRetriesExceeded(f"Operation failed after {self.max_retries} retries") from ex
                        
                        # Add jitter to prevent thundering herd
                        jitter = random.uniform(0.1, 0.5)
                        actual_delay = delay + jitter
                        
                        print(f"Operation failed (attempt {retry_count}/{self.max_retries}): {ex}. Retrying in {actual_delay:.2f} seconds.")
                        
                        await asyncio.sleep(actual_delay)
                        delay *= self.backoff_multiplier
                
                raise RuntimeError("Retry logic should not reach this point")
            
            return wrapper
        return decorator

# Usage
retry_handler = eAdapterRetryHandler(max_retries=5, base_delay=2.0)

@retry_handler.retry(should_retry=lambda ex: isinstance(ex, (ConnectionError, TimeoutError)))
async def send_eAdapter_message(message: str) -> str:
    # Implementation here
    pass

Circuit Breaker Pattern

C# Circuit Breaker:

public class eAdapterCircuitBreaker
{
    private readonly int _failureThreshold;
    private readonly TimeSpan _timeout;
    private int _failureCount;
    private DateTime _lastFailureTime;
    private CircuitState _state = CircuitState.Closed;
    
    public enum CircuitState
    {
        Closed,
        Open,
        HalfOpen
    }
    
    public eAdapterCircuitBreaker(int failureThreshold = 5, TimeSpan timeout = TimeSpan.FromMinutes(1))
    {
        _failureThreshold = failureThreshold;
        _timeout = timeout;
    }
    
    public async Task<T> Execute<T>(Func<Task<T>> operation)
    {
        if (_state == CircuitState.Open)
        {
            if (DateTime.UtcNow - _lastFailureTime > _timeout)
            {
                _state = CircuitState.HalfOpen;
            }
            else
            {
                throw new CircuitBreakerOpenException("Circuit breaker is open");
            }
        }
        
        try
        {
            var result = await operation();
            OnSuccess();
            return result;
        }
        catch (Exception ex)
        {
            OnFailure();
            throw;
        }
    }
    
    private void OnSuccess()
    {
        _failureCount = 0;
        _state = CircuitState.Closed;
    }
    
    private void OnFailure()
    {
        _failureCount++;
        _lastFailureTime = DateTime.UtcNow;
        
        if (_failureCount >= _failureThreshold)
        {
            _state = CircuitState.Open;
        }
    }
}

Message Validation and Schema Management

XML Schema Validation

C# Schema Validation:

public class eAdapterMessageValidator
{
    private readonly XmlSchemaSet _schemaSet;
    
    public eAdapterMessageValidator()
    {
        _schemaSet = new XmlSchemaSet();
        LoadSchemas();
    }
    
    private void LoadSchemas()
    {
        // Load Universal Shipment schema
        _schemaSet.Add("urn:cargowise:eadapter:universalshipment", 
            "schemas/UniversalShipment.xsd");
        
        // Load Consol schema
        _schemaSet.Add("urn:cargowise:eadapter:consol", 
            "schemas/Consol.xsd");
        
        // Load Forward schema
        _schemaSet.Add("urn:cargowise:eadapter:forward", 
            "schemas/Forward.xsd");
    }
    
    public ValidationResult ValidateMessage(string xmlMessage)
    {
        var settings = new XmlReaderSettings
        {
            Schemas = _schemaSet,
            ValidationType = ValidationType.Schema,
            ValidationFlags = XmlSchemaValidationFlags.ProcessSchemaLocation
        };
        
        var errors = new List<string>();
        settings.ValidationEventHandler += (sender, e) =>
        {
            errors.Add($"Line {e.Exception.LineNumber}: {e.Exception.Message}");
        };
        
        try
        {
            using (var reader = XmlReader.Create(new StringReader(xmlMessage), settings))
            {
                while (reader.Read()) { }
            }
            
            return new ValidationResult
            {
                IsValid = errors.Count == 0,
                Errors = errors
            };
        }
        catch (Exception ex)
        {
            return new ValidationResult
            {
                IsValid = false,
                Errors = new List<string> { ex.Message }
            };
        }
    }
}

Python Schema Validation:

import xml.etree.ElementTree as ET
from lxml import etree
from typing import List, Dict, Any

class eAdapterMessageValidator:
    def __init__(self):
        self.schemas = {}
        self.load_schemas()
    
    def load_schemas(self):
        """Load XML schemas for validation"""
        schema_files = {
            'universalshipment': 'schemas/UniversalShipment.xsd',
            'consol': 'schemas/Consol.xsd',
            'forward': 'schemas/Forward.xsd'
        }
        
        for name, file_path in schema_files.items():
            with open(file_path, 'r') as f:
                schema_doc = etree.parse(f)
                self.schemas[name] = etree.XMLSchema(schema_doc)
    
    def validate_message(self, xml_message: str, message_type: str) -> Dict[str, Any]:
        """Validate XML message against appropriate schema"""
        try:
            # Parse XML
            root = etree.fromstring(xml_message.encode('utf-8'))
            
            # Get appropriate schema
            schema = self.schemas.get(message_type)
            if not schema:
                return {
                    'valid': False,
                    'errors': [f'Unknown message type: {message_type}']
                }
            
            # Validate
            schema.assertValid(root)
            
            return {
                'valid': True,
                'errors': []
            }
            
        except etree.XMLSyntaxError as e:
            return {
                'valid': False,
                'errors': [f'XML syntax error: {e.msg}']
            }
        except etree.DocumentInvalid as e:
            return {
                'valid': False,
                'errors': [f'Schema validation error: {e.msg}']
            }
        except Exception as e:
            return {
                'valid': False,
                'errors': [f'Validation error: {str(e)}']
            }

Performance Optimization Strategies

Message Batching

C# Message Batching:

public class eAdapterMessageBatcher
{
    private readonly int _batchSize;
    private readonly TimeSpan _batchTimeout;
    private readonly List<eAdapterMessage> _pendingMessages;
    private readonly Timer _batchTimer;
    
    public eAdapterMessageBatcher(int batchSize = 100, TimeSpan batchTimeout = TimeSpan.FromSeconds(30))
    {
        _batchSize = batchSize;
        _batchTimeout = batchTimeout;
        _pendingMessages = new List<eAdapterMessage>();
        _batchTimer = new Timer(ProcessBatch, null, batchTimeout, Timeout.InfiniteTimeSpan);
    }
    
    public async Task AddMessage(eAdapterMessage message)
    {
        lock (_pendingMessages)
        {
            _pendingMessages.Add(message);
            
            if (_pendingMessages.Count >= _batchSize)
            {
                _ = Task.Run(ProcessBatch);
            }
        }
    }
    
    private async Task ProcessBatch()
    {
        List<eAdapterMessage> messagesToProcess;
        
        lock (_pendingMessages)
        {
            messagesToProcess = new List<eAdapterMessage>(_pendingMessages);
            _pendingMessages.Clear();
        }
        
        if (messagesToProcess.Any())
        {
            await SendBatch(messagesToProcess);
        }
    }
    
    private async Task SendBatch(List<eAdapterMessage> messages)
    {
        var batchXml = CreateBatchXml(messages);
        await eAdapterClient.SendMessageAsync(batchXml);
    }
    
    private string CreateBatchXml(List<eAdapterMessage> messages)
    {
        var batch = new XElement("MessageBatch",
            new XAttribute("BatchId", Guid.NewGuid().ToString()),
            new XAttribute("MessageCount", messages.Count),
            new XAttribute("Timestamp", DateTime.UtcNow),
            messages.Select(m => m.ToXml())
        );
        
        return batch.ToString();
    }
}

Connection Pooling

C# Connection Pooling:

public class eAdapterConnectionPool
{
    private readonly ConcurrentQueue<eAdapterConnection> _availableConnections;
    private readonly SemaphoreSlim _semaphore;
    private readonly int _maxConnections;
    
    public eAdapterConnectionPool(int maxConnections = 10)
    {
        _maxConnections = maxConnections;
        _availableConnections = new ConcurrentQueue<eAdapterConnection>();
        _semaphore = new SemaphoreSlim(maxConnections, maxConnections);
    }
    
    public async Task<T> ExecuteWithConnection<T>(Func<eAdapterConnection, Task<T>> operation)
    {
        await _semaphore.WaitAsync();
        
        try
        {
            if (!_availableConnections.TryDequeue(out var connection))
            {
                connection = await CreateConnection();
            }
            
            return await operation(connection);
        }
        finally
        {
            if (connection != null && connection.IsHealthy)
            {
                _availableConnections.Enqueue(connection);
            }
            
            _semaphore.Release();
        }
    }
    
    private async Task<eAdapterConnection> CreateConnection()
    {
        // Create new connection
        return new eAdapterConnection();
    }
}

Monitoring and Logging

Comprehensive Logging Strategy

C# Logging Implementation:

public class eAdapterLogger
{
    private readonly ILogger _logger;
    private readonly IConfiguration _configuration;
    
    public eAdapterLogger(ILogger<eAdapterLogger> logger, IConfiguration configuration)
    {
        _logger = logger;
        _configuration = configuration;
    }
    
    public void LogMessageSent(string messageId, string messageType, TimeSpan duration)
    {
        _logger.LogInformation("eAdapter message sent - MessageId: {MessageId}, Type: {MessageType}, Duration: {Duration}ms",
            messageId, messageType, duration.TotalMilliseconds);
    }
    
    public void LogMessageReceived(string messageId, string messageType, bool success)
    {
        _logger.LogInformation("eAdapter message received - MessageId: {MessageId}, Type: {MessageType}, Success: {Success}",
            messageId, messageType, success);
    }
    
    public void LogError(string messageId, Exception exception, string context = null)
    {
        _logger.LogError(exception, "eAdapter error - MessageId: {MessageId}, Context: {Context}",
            messageId, context ?? "Unknown");
    }
    
    public void LogPerformanceMetrics(string operation, TimeSpan duration, int messageCount = 1)
    {
        var metrics = new
        {
            Operation = operation,
            Duration = duration.TotalMilliseconds,
            MessageCount = messageCount,
            MessagesPerSecond = messageCount / duration.TotalSeconds
        };
        
        _logger.LogInformation("eAdapter performance - {@Metrics}", metrics);
    }
}

Health Monitoring

C# Health Monitoring:

public class eAdapterHealthMonitor : IHealthCheck
{
    private readonly eAdapterClient _client;
    private readonly ILogger<eAdapterHealthMonitor> _logger;
    
    public eAdapterHealthMonitor(eAdapterClient client, ILogger<eAdapterHealthMonitor> logger)
    {
        _client = client;
        _logger = logger;
    }
    
    public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        try
        {
            var pingMessage = CreatePingMessage();
            var response = await _client.SendMessageAsync(pingMessage);
            
            if (response.IsSuccess)
            {
                return HealthCheckResult.Healthy("eAdapter is responding");
            }
            else
            {
                return HealthCheckResult.Unhealthy($"eAdapter returned error: {response.ErrorMessage}");
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "eAdapter health check failed");
            return HealthCheckResult.Unhealthy($"eAdapter health check failed: {ex.Message}");
        }
    }
    
    private string CreatePingMessage()
    {
        return @"<?xml version=""1.0"" encoding=""UTF-8""?>
<Ping>
    <Header>
        <MessageId>PING-" + Guid.NewGuid().ToString() + @"</MessageId>
        <Timestamp>" + DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ") + @"</Timestamp>
    </Header>
</Ping>";
    }
}

Best Practices and Common Pitfalls

Message Design Best Practices

  1. Use Descriptive Message IDs

    • Include timestamp and source system
    • Use UUIDs for uniqueness
    • Example: MSG-2025-001-ABC-SYSTEM
  2. Implement Proper Error Handling

    • Always check response status
    • Log errors with context
    • Implement retry logic for transient failures
  3. Validate Messages Before Sending

    • Use XML schema validation
    • Check required fields
    • Validate data types and formats
  4. Handle Timeouts Gracefully

    • Set appropriate timeout values
    • Implement circuit breaker patterns
    • Monitor response times

Common Integration Pitfalls

Pitfall 1: Ignoring Message Ordering

// BAD: Messages may arrive out of order
await SendMessageAsync(shipmentMessage);
await SendMessageAsync(consolMessage);

// GOOD: Use sequence numbers or timestamps
var shipment = new UniversalShipmentMessage
{
    SequenceNumber = GetNextSequenceNumber(),
    // ... other properties
};

Pitfall 2: Not Handling Duplicate Messages

// BAD: No duplicate detection
public async Task ProcessMessage(string messageId, string xmlData)
{
    // Process message without checking for duplicates
}

// GOOD: Implement idempotency
public async Task ProcessMessage(string messageId, string xmlData)
{
    if (await IsMessageAlreadyProcessed(messageId))
    {
        _logger.LogInformation("Message {MessageId} already processed, skipping", messageId);
        return;
    }
    
    // Process message
    await ProcessMessageInternal(xmlData);
    await MarkMessageAsProcessed(messageId);
}

Pitfall 3: Poor Error Recovery

// BAD: Generic error handling
try
{
    await SendMessageAsync(message);
}
catch (Exception ex)
{
    _logger.LogError(ex, "Failed to send message");
    // No recovery strategy
}

// GOOD: Specific error handling with recovery
try
{
    await SendMessageAsync(message);
}
catch (eAdapterValidationException ex)
{
    _logger.LogError(ex, "Validation failed: {Error}", ex.Message);
    // Fix validation issues and retry
}
catch (eAdapterSystemException ex)
{
    _logger.LogError(ex, "System error: {Error}", ex.Message);
    // Queue for retry with backoff
    await QueueForRetry(message, ex.RetryAfter);
}

Implementation Guide

Step 1: Environment Setup

  1. Install CargoWise eAdapter Client

    # C# - Install via NuGet
    dotnet add package CargoWise.eAdapter.Client
    
    # Python - Install via pip
    pip install cargowise-eadapter-client
    
  2. Configure Connection Settings

    {
      "eAdapter": {
        "Endpoint": "https://eadapter.cargowise.com/api",
        "Username": "your-username",
        "Password": "your-password",
        "Timeout": 30000,
        "RetryCount": 3,
        "RetryDelay": 5000
      }
    }
    

Step 2: Basic Integration Setup

C# Setup:

public class eAdapterService
{
    private readonly eAdapterClient _client;
    private readonly eAdapterMessageValidator _validator;
    private readonly eAdapterRetryHandler _retryHandler;
    
    public eAdapterService(IConfiguration configuration)
    {
        var settings = configuration.GetSection("eAdapter").Get<eAdapterSettings>();
        _client = new eAdapterClient(settings);
        _validator = new eAdapterMessageValidator();
        _retryHandler = new eAdapterRetryHandler();
    }
    
    public async Task<string> SendUniversalShipment(UniversalShipmentMessage message)
    {
        var xml = SerializeToXml(message);
        
        // Validate message
        var validation = _validator.ValidateMessage(xml, "universalshipment");
        if (!validation.IsValid)
        {
            throw new ValidationException($"Message validation failed: {string.Join(", ", validation.Errors)}");
        }
        
        // Send with retry logic
        return await _retryHandler.ExecuteWithRetry(async () =>
        {
            return await _client.SendMessageAsync(xml);
        });
    }
}

Step 3: Testing Strategy

  1. Unit Tests

    • Test message serialization/deserialization
    • Test validation logic
    • Test error handling
  2. Integration Tests

    • Test with CargoWise sandbox environment
    • Test error scenarios
    • Test performance under load
  3. End-to-End Tests

    • Test complete message flow
    • Test with real data
    • Test error recovery

Conclusion

CargoWise eAdapter integration requires careful attention to message design, error handling, and performance optimization. By following the patterns and best practices outlined in this guide, you can build robust, scalable integrations that can handle the complex requirements of modern freight forwarding operations.

Key Takeaways:

  1. Message Design: Use proper XML schemas and validation
  2. Error Handling: Implement comprehensive error handling and retry logic
  3. Performance: Use batching, connection pooling, and monitoring
  4. Testing: Implement thorough testing at all levels
  5. Monitoring: Use comprehensive logging and health checks

Next Steps:

  1. Set up development environment with CargoWise sandbox
  2. Implement basic message types (Universal Shipment, Consol, Forward)
  3. Add error handling and retry logic for production resilience
  4. Implement monitoring and logging for operational visibility
  5. Test thoroughly before deploying to production

For more CargoWise integration guidance and implementation support, explore our CargoWise Integration Services or contact our team for personalized consulting.

FAQ

Q: What is the difference between eAdapter and CargoWise REST API? A: eAdapter uses XML message-based communication with asynchronous processing, while the REST API uses HTTP with JSON/XML for synchronous operations. eAdapter is better for high-volume, reliable message processing, while REST API is better for real-time queries and simple operations.

Q: How do I handle message ordering in eAdapter? A: Use sequence numbers or timestamps in your message headers. CargoWise processes messages in the order they are received, so ensure your external system sends messages in the correct sequence.

Q: What are the common eAdapter error codes? A: Common error codes include VALIDATION_ERROR (invalid message format), SYSTEM_ERROR (CargoWise system issues), TIMEOUT_ERROR (message processing timeout), and NETWORK_ERROR (connection issues).

Q: How can I monitor eAdapter performance? A: Implement comprehensive logging, use health checks, monitor message processing times, track error rates, and set up alerts for system issues.

Q: Is eAdapter suitable for real-time integrations? A: eAdapter is designed for reliable message processing rather than real-time operations. For real-time needs, consider using CargoWise REST API or webhooks for event notifications.

Related posts