xpeditis2.0/apps/backend/docs/API.md
David-Henri ARNAUD 1044900e98 feature phase
2025-10-08 16:56:27 +02:00

578 lines
14 KiB
Markdown

# 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