xpeditis2.0/apps/backend/docs/API.md
2025-10-20 12:30:08 +02:00

14 KiB

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

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:

{
  "origin": "NLRTM",
  "destination": "CNSHA",
  "containerType": "40HC",
  "mode": "FCL",
  "departureDate": "2025-02-15",
  "quantity": 2,
  "weight": 20000,
  "isHazmat": false
}

Response: 200 OK

{
  "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

{
  "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:

{
  "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

{
  "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

{
  "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

{
  "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:

{
  "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

{
  "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

{
  "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):

{
  "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:

{
  "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:

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:


API Version: v1.0.0 Last Updated: February 2025 Changelog: See CHANGELOG.md