# Xpeditis API Documentation Complete API reference for the Xpeditis maritime freight booking platform. **Base URL:** `https://api.xpeditis.com` (Production) | `http://localhost:4000` (Development) **API Version:** v1 **Last Updated:** February 2025 --- ## 📑 Table of Contents - [Authentication](#authentication) - [Rate Search API](#rate-search-api) - [Bookings API](#bookings-api) - [Error Handling](#error-handling) - [Rate Limiting](#rate-limiting) - [Webhooks](#webhooks) --- ## 🔐 Authentication **Status:** To be implemented in Phase 2 The API will use OAuth2 + JWT for authentication: - Access tokens valid for 15 minutes - Refresh tokens valid for 7 days - All endpoints (except auth) require `Authorization: Bearer {token}` header **Planned Endpoints:** - `POST /auth/register` - Register new user - `POST /auth/login` - Login and receive tokens - `POST /auth/refresh` - Refresh access token - `POST /auth/logout` - Invalidate tokens --- ## 🔍 Rate Search API ### Search Shipping Rates Search for available shipping rates from multiple carriers. **Endpoint:** `POST /api/v1/rates/search` **Authentication:** Required (Phase 2) **Request Headers:** ``` Content-Type: application/json ``` **Request Body:** | Field | Type | Required | Description | Example | |-------|------|----------|-------------|---------| | `origin` | string | ✅ | Origin port code (UN/LOCODE, 5 chars) | `"NLRTM"` | | `destination` | string | ✅ | Destination port code (UN/LOCODE, 5 chars) | `"CNSHA"` | | `containerType` | string | ✅ | Container type | `"40HC"` | | `mode` | string | ✅ | Shipping mode | `"FCL"` or `"LCL"` | | `departureDate` | string | ✅ | ISO 8601 date | `"2025-02-15"` | | `quantity` | number | ❌ | Number of containers (default: 1) | `2` | | `weight` | number | ❌ | Total cargo weight in kg | `20000` | | `volume` | number | ❌ | Total cargo volume in m³ | `50.5` | | `isHazmat` | boolean | ❌ | Is hazardous material (default: false) | `false` | | `imoClass` | string | ❌ | IMO hazmat class (required if isHazmat=true) | `"3"` | **Container Types:** - `20DRY` - 20ft Dry Container - `20HC` - 20ft High Cube - `40DRY` - 40ft Dry Container - `40HC` - 40ft High Cube - `40REEFER` - 40ft Refrigerated - `45HC` - 45ft High Cube **Request Example:** ```json { "origin": "NLRTM", "destination": "CNSHA", "containerType": "40HC", "mode": "FCL", "departureDate": "2025-02-15", "quantity": 2, "weight": 20000, "isHazmat": false } ``` **Response:** `200 OK` ```json { "quotes": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "carrierId": "550e8400-e29b-41d4-a716-446655440001", "carrierName": "Maersk Line", "carrierCode": "MAERSK", "origin": { "code": "NLRTM", "name": "Rotterdam", "country": "Netherlands" }, "destination": { "code": "CNSHA", "name": "Shanghai", "country": "China" }, "pricing": { "baseFreight": 1500.0, "surcharges": [ { "type": "BAF", "description": "Bunker Adjustment Factor", "amount": 150.0, "currency": "USD" }, { "type": "CAF", "description": "Currency Adjustment Factor", "amount": 50.0, "currency": "USD" } ], "totalAmount": 1700.0, "currency": "USD" }, "containerType": "40HC", "mode": "FCL", "etd": "2025-02-15T10:00:00Z", "eta": "2025-03-17T14:00:00Z", "transitDays": 30, "route": [ { "portCode": "NLRTM", "portName": "Port of Rotterdam", "departure": "2025-02-15T10:00:00Z", "vesselName": "MAERSK ESSEX", "voyageNumber": "025W" }, { "portCode": "CNSHA", "portName": "Port of Shanghai", "arrival": "2025-03-17T14:00:00Z" } ], "availability": 85, "frequency": "Weekly", "vesselType": "Container Ship", "co2EmissionsKg": 12500.5, "validUntil": "2025-02-15T10:15:00Z", "createdAt": "2025-02-15T10:00:00Z" } ], "count": 5, "origin": "NLRTM", "destination": "CNSHA", "departureDate": "2025-02-15", "containerType": "40HC", "mode": "FCL", "fromCache": false, "responseTimeMs": 234 } ``` **Validation Errors:** `400 Bad Request` ```json { "statusCode": 400, "message": [ "Origin must be a valid 5-character UN/LOCODE (e.g., NLRTM)", "Departure date must be a valid ISO 8601 date string" ], "error": "Bad Request" } ``` **Caching:** - Results are cached for **15 minutes** - Cache key format: `rates:{origin}:{destination}:{date}:{containerType}:{mode}` - Cache hit indicated by `fromCache: true` in response - Top 100 trade lanes pre-cached on application startup **Performance:** - Target: <2 seconds (90% of requests with cache) - Cache hit: <100ms - Carrier API timeout: 5 seconds per carrier - Circuit breaker activates after 50% error rate --- ## 📦 Bookings API ### Create Booking Create a new booking based on a rate quote. **Endpoint:** `POST /api/v1/bookings` **Authentication:** Required (Phase 2) **Request Headers:** ``` Content-Type: application/json ``` **Request Body:** ```json { "rateQuoteId": "550e8400-e29b-41d4-a716-446655440000", "shipper": { "name": "Acme Corporation", "address": { "street": "123 Main Street", "city": "Rotterdam", "postalCode": "3000 AB", "country": "NL" }, "contactName": "John Doe", "contactEmail": "john.doe@acme.com", "contactPhone": "+31612345678" }, "consignee": { "name": "Shanghai Imports Ltd", "address": { "street": "456 Trade Avenue", "city": "Shanghai", "postalCode": "200000", "country": "CN" }, "contactName": "Jane Smith", "contactEmail": "jane.smith@shanghai-imports.cn", "contactPhone": "+8613812345678" }, "cargoDescription": "Electronics and consumer goods for retail distribution", "containers": [ { "type": "40HC", "containerNumber": "ABCU1234567", "vgm": 22000, "sealNumber": "SEAL123456" } ], "specialInstructions": "Please handle with care. Delivery before 5 PM." } ``` **Field Validations:** | Field | Validation | Error Message | |-------|------------|---------------| | `rateQuoteId` | Valid UUID v4 | "Rate quote ID must be a valid UUID" | | `shipper.name` | Min 2 characters | "Name must be at least 2 characters" | | `shipper.contactEmail` | Valid email | "Contact email must be a valid email address" | | `shipper.contactPhone` | E.164 format | "Contact phone must be a valid international phone number" | | `shipper.address.country` | ISO 3166-1 alpha-2 | "Country must be a valid 2-letter ISO country code" | | `cargoDescription` | Min 10 characters | "Cargo description must be at least 10 characters" | | `containers[].containerNumber` | 4 letters + 7 digits (optional) | "Container number must be 4 letters followed by 7 digits" | **Response:** `201 Created` ```json { "id": "550e8400-e29b-41d4-a716-446655440001", "bookingNumber": "WCM-2025-ABC123", "status": "draft", "shipper": { ... }, "consignee": { ... }, "cargoDescription": "Electronics and consumer goods for retail distribution", "containers": [ { "id": "550e8400-e29b-41d4-a716-446655440002", "type": "40HC", "containerNumber": "ABCU1234567", "vgm": 22000, "sealNumber": "SEAL123456" } ], "specialInstructions": "Please handle with care. Delivery before 5 PM.", "rateQuote": { "id": "550e8400-e29b-41d4-a716-446655440000", "carrierName": "Maersk Line", "carrierCode": "MAERSK", "origin": { ... }, "destination": { ... }, "pricing": { ... }, "containerType": "40HC", "mode": "FCL", "etd": "2025-02-15T10:00:00Z", "eta": "2025-03-17T14:00:00Z", "transitDays": 30 }, "createdAt": "2025-02-15T10:00:00Z", "updatedAt": "2025-02-15T10:00:00Z" } ``` **Booking Number Format:** - Pattern: `WCM-YYYY-XXXXXX` - Example: `WCM-2025-ABC123` - `WCM` = WebCargo Maritime prefix - `YYYY` = Current year - `XXXXXX` = 6 random alphanumeric characters (excludes ambiguous: 0, O, 1, I) **Booking Statuses:** - `draft` - Initial state, can be modified - `pending_confirmation` - Submitted for carrier confirmation - `confirmed` - Confirmed by carrier - `in_transit` - Shipment in progress - `delivered` - Shipment delivered (final) - `cancelled` - Booking cancelled (final) --- ### Get Booking by ID **Endpoint:** `GET /api/v1/bookings/:id` **Path Parameters:** - `id` (UUID) - Booking ID **Response:** `200 OK` Returns same structure as Create Booking response. **Error:** `404 Not Found` ```json { "statusCode": 404, "message": "Booking 550e8400-e29b-41d4-a716-446655440001 not found", "error": "Not Found" } ``` --- ### Get Booking by Number **Endpoint:** `GET /api/v1/bookings/number/:bookingNumber` **Path Parameters:** - `bookingNumber` (string) - Booking number (e.g., `WCM-2025-ABC123`) **Response:** `200 OK` Returns same structure as Create Booking response. --- ### List Bookings **Endpoint:** `GET /api/v1/bookings` **Query Parameters:** | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `page` | number | ❌ | 1 | Page number (1-based) | | `pageSize` | number | ❌ | 20 | Items per page (max: 100) | | `status` | string | ❌ | - | Filter by status | **Example:** `GET /api/v1/bookings?page=2&pageSize=10&status=draft` **Response:** `200 OK` ```json { "bookings": [ { "id": "550e8400-e29b-41d4-a716-446655440001", "bookingNumber": "WCM-2025-ABC123", "status": "draft", "shipperName": "Acme Corporation", "consigneeName": "Shanghai Imports Ltd", "originPort": "NLRTM", "destinationPort": "CNSHA", "carrierName": "Maersk Line", "etd": "2025-02-15T10:00:00Z", "eta": "2025-03-17T14:00:00Z", "totalAmount": 1700.0, "currency": "USD", "createdAt": "2025-02-15T10:00:00Z" } ], "total": 25, "page": 2, "pageSize": 10, "totalPages": 3 } ``` --- ## ❌ Error Handling ### Error Response Format All errors follow this structure: ```json { "statusCode": 400, "message": "Error description or array of validation errors", "error": "Bad Request" } ``` ### HTTP Status Codes | Code | Description | When Used | |------|-------------|-----------| | `200` | OK | Successful GET request | | `201` | Created | Successful POST (resource created) | | `400` | Bad Request | Validation errors, malformed request | | `401` | Unauthorized | Missing or invalid authentication | | `403` | Forbidden | Insufficient permissions | | `404` | Not Found | Resource doesn't exist | | `429` | Too Many Requests | Rate limit exceeded | | `500` | Internal Server Error | Unexpected server error | | `503` | Service Unavailable | Carrier API down, circuit breaker open | ### Validation Errors ```json { "statusCode": 400, "message": [ "Origin must be a valid 5-character UN/LOCODE (e.g., NLRTM)", "Container type must be one of: 20DRY, 20HC, 40DRY, 40HC, 40REEFER, 45HC", "Quantity must be at least 1" ], "error": "Bad Request" } ``` ### Rate Limit Error ```json { "statusCode": 429, "message": "Too many requests. Please try again in 60 seconds.", "error": "Too Many Requests", "retryAfter": 60 } ``` ### Circuit Breaker Error When a carrier API is unavailable (circuit breaker open): ```json { "statusCode": 503, "message": "Maersk API is temporarily unavailable. Please try again later.", "error": "Service Unavailable", "retryAfter": 30 } ``` --- ## ⚡ Rate Limiting **Status:** To be implemented in Phase 2 **Planned Limits:** - 100 requests per minute per API key - 1000 requests per hour per API key - Rate search: 20 requests per minute (resource-intensive) **Headers:** ``` X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1612345678 ``` --- ## 🔔 Webhooks **Status:** To be implemented in Phase 3 Planned webhook events: - `booking.confirmed` - Booking confirmed by carrier - `booking.in_transit` - Shipment departed - `booking.delivered` - Shipment delivered - `booking.delayed` - Shipment delayed - `booking.cancelled` - Booking cancelled **Webhook Payload Example:** ```json { "event": "booking.confirmed", "timestamp": "2025-02-15T10:30:00Z", "data": { "bookingId": "550e8400-e29b-41d4-a716-446655440001", "bookingNumber": "WCM-2025-ABC123", "status": "confirmed", "confirmedAt": "2025-02-15T10:30:00Z" } } ``` --- ## 📊 Best Practices ### Pagination Always use pagination for list endpoints to avoid performance issues: ``` GET /api/v1/bookings?page=1&pageSize=20 ``` ### Date Formats All dates use ISO 8601 format: - Request: `"2025-02-15"` (date only) - Response: `"2025-02-15T10:00:00Z"` (with timezone) ### Port Codes Use UN/LOCODE (5-character codes): - Rotterdam: `NLRTM` - Shanghai: `CNSHA` - Los Angeles: `USLAX` - Hamburg: `DEHAM` Find port codes: https://unece.org/trade/cefact/unlocode-code-list-country-and-territory ### Error Handling Always check `statusCode` and handle errors gracefully: ```javascript try { const response = await fetch('/api/v1/rates/search', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(searchParams) }); if (!response.ok) { const error = await response.json(); console.error('API Error:', error.message); return; } const data = await response.json(); // Process data } catch (error) { console.error('Network Error:', error); } ``` --- ## 📞 Support For API support: - Email: api-support@xpeditis.com - Documentation: https://docs.xpeditis.com - Status Page: https://status.xpeditis.com --- **API Version:** v1.0.0 **Last Updated:** February 2025 **Changelog:** See CHANGELOG.md