728 lines
15 KiB
Markdown
728 lines
15 KiB
Markdown
# Carrier Portal API Documentation
|
|
|
|
**Version**: 1.0
|
|
**Base URL**: `http://localhost:4000/api/v1`
|
|
**Last Updated**: 2025-12-04
|
|
|
|
## Table of Contents
|
|
|
|
1. [Overview](#overview)
|
|
2. [Authentication](#authentication)
|
|
3. [API Endpoints](#api-endpoints)
|
|
- [Carrier Authentication](#carrier-authentication)
|
|
- [Carrier Dashboard](#carrier-dashboard)
|
|
- [Booking Management](#booking-management)
|
|
- [Document Management](#document-management)
|
|
4. [Data Models](#data-models)
|
|
5. [Error Handling](#error-handling)
|
|
6. [Examples](#examples)
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
The Carrier Portal API provides endpoints for transportation carriers (transporteurs) to:
|
|
- Authenticate and manage their accounts
|
|
- View dashboard statistics
|
|
- Manage booking requests from clients
|
|
- Accept or reject booking requests
|
|
- Download shipment documents
|
|
- Track their performance metrics
|
|
|
|
All endpoints require JWT authentication except for the public authentication endpoints.
|
|
|
|
---
|
|
|
|
## Authentication
|
|
|
|
### Authentication Header
|
|
|
|
All protected endpoints require a Bearer token in the Authorization header:
|
|
|
|
```
|
|
Authorization: Bearer <access_token>
|
|
```
|
|
|
|
### Token Management
|
|
|
|
- **Access Token**: Valid for 15 minutes
|
|
- **Refresh Token**: Valid for 7 days
|
|
- **Auto-Login Token**: Valid for 1 hour (for magic link authentication)
|
|
|
|
---
|
|
|
|
## API Endpoints
|
|
|
|
### Carrier Authentication
|
|
|
|
#### 1. Login
|
|
|
|
**Endpoint**: `POST /carrier-auth/login`
|
|
|
|
**Description**: Authenticate a carrier with email and password.
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"email": "carrier@example.com",
|
|
"password": "SecurePassword123!"
|
|
}
|
|
```
|
|
|
|
**Response** (200 OK):
|
|
```json
|
|
{
|
|
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
"carrier": {
|
|
"id": "carrier-uuid",
|
|
"companyName": "Transport Express",
|
|
"email": "carrier@example.com"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Errors**:
|
|
- `401 Unauthorized`: Invalid credentials
|
|
- `401 Unauthorized`: Account is inactive
|
|
- `400 Bad Request`: Validation error
|
|
|
|
---
|
|
|
|
#### 2. Get Current Carrier Profile
|
|
|
|
**Endpoint**: `GET /carrier-auth/me`
|
|
|
|
**Description**: Retrieve the authenticated carrier's profile information.
|
|
|
|
**Headers**:
|
|
```
|
|
Authorization: Bearer <access_token>
|
|
```
|
|
|
|
**Response** (200 OK):
|
|
```json
|
|
{
|
|
"id": "carrier-uuid",
|
|
"userId": "user-uuid",
|
|
"companyName": "Transport Express",
|
|
"email": "carrier@example.com",
|
|
"role": "CARRIER",
|
|
"organizationId": "org-uuid",
|
|
"phone": "+33612345678",
|
|
"website": "https://transport-express.com",
|
|
"city": "Paris",
|
|
"country": "France",
|
|
"isVerified": true,
|
|
"isActive": true,
|
|
"totalBookingsAccepted": 45,
|
|
"totalBookingsRejected": 5,
|
|
"acceptanceRate": 90.0,
|
|
"totalRevenueUsd": 125000,
|
|
"totalRevenueEur": 112500,
|
|
"preferredCurrency": "EUR",
|
|
"lastLoginAt": "2025-12-04T10:30:00Z"
|
|
}
|
|
```
|
|
|
|
**Errors**:
|
|
- `401 Unauthorized`: Invalid or expired token
|
|
|
|
---
|
|
|
|
#### 3. Change Password
|
|
|
|
**Endpoint**: `PATCH /carrier-auth/change-password`
|
|
|
|
**Description**: Change the carrier's password.
|
|
|
|
**Headers**:
|
|
```
|
|
Authorization: Bearer <access_token>
|
|
```
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"oldPassword": "OldPassword123!",
|
|
"newPassword": "NewPassword123!"
|
|
}
|
|
```
|
|
|
|
**Response** (200 OK):
|
|
```json
|
|
{
|
|
"message": "Password changed successfully"
|
|
}
|
|
```
|
|
|
|
**Errors**:
|
|
- `401 Unauthorized`: Invalid old password
|
|
- `400 Bad Request`: Password validation failed
|
|
|
|
---
|
|
|
|
#### 4. Request Password Reset
|
|
|
|
**Endpoint**: `POST /carrier-auth/request-password-reset`
|
|
|
|
**Description**: Request a password reset (generates temporary password).
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"email": "carrier@example.com"
|
|
}
|
|
```
|
|
|
|
**Response** (200 OK):
|
|
```json
|
|
{
|
|
"message": "If this email exists, a password reset will be sent"
|
|
}
|
|
```
|
|
|
|
**Note**: For security, the response is the same whether the email exists or not.
|
|
|
|
---
|
|
|
|
#### 5. Verify Auto-Login Token
|
|
|
|
**Endpoint**: `POST /carrier-auth/verify-auto-login`
|
|
|
|
**Description**: Verify an auto-login token from email magic link.
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
}
|
|
```
|
|
|
|
**Response** (200 OK):
|
|
```json
|
|
{
|
|
"userId": "user-uuid",
|
|
"carrierId": "carrier-uuid"
|
|
}
|
|
```
|
|
|
|
**Errors**:
|
|
- `401 Unauthorized`: Invalid or expired token
|
|
|
|
---
|
|
|
|
### Carrier Dashboard
|
|
|
|
#### 6. Get Dashboard Statistics
|
|
|
|
**Endpoint**: `GET /carrier-dashboard/stats`
|
|
|
|
**Description**: Retrieve carrier dashboard statistics including bookings count, revenue, and recent activities.
|
|
|
|
**Headers**:
|
|
```
|
|
Authorization: Bearer <access_token>
|
|
```
|
|
|
|
**Response** (200 OK):
|
|
```json
|
|
{
|
|
"totalBookings": 50,
|
|
"pendingBookings": 5,
|
|
"acceptedBookings": 42,
|
|
"rejectedBookings": 3,
|
|
"acceptanceRate": 93.3,
|
|
"totalRevenue": {
|
|
"usd": 125000,
|
|
"eur": 112500
|
|
},
|
|
"recentActivities": [
|
|
{
|
|
"id": "activity-uuid",
|
|
"type": "BOOKING_ACCEPTED",
|
|
"description": "Booking #12345 accepted",
|
|
"createdAt": "2025-12-04T09:15:00Z",
|
|
"bookingId": "booking-uuid"
|
|
},
|
|
{
|
|
"id": "activity-uuid-2",
|
|
"type": "DOCUMENT_DOWNLOADED",
|
|
"description": "Downloaded invoice.pdf",
|
|
"createdAt": "2025-12-04T08:30:00Z",
|
|
"bookingId": "booking-uuid-2"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Errors**:
|
|
- `401 Unauthorized`: Invalid or expired token
|
|
- `404 Not Found`: Carrier not found
|
|
|
|
---
|
|
|
|
#### 7. Get Carrier Bookings (Paginated)
|
|
|
|
**Endpoint**: `GET /carrier-dashboard/bookings`
|
|
|
|
**Description**: Retrieve a paginated list of bookings for the carrier.
|
|
|
|
**Headers**:
|
|
```
|
|
Authorization: Bearer <access_token>
|
|
```
|
|
|
|
**Query Parameters**:
|
|
- `page` (number, optional): Page number (default: 1)
|
|
- `limit` (number, optional): Items per page (default: 10)
|
|
- `status` (string, optional): Filter by status (PENDING, ACCEPTED, REJECTED)
|
|
|
|
**Example Request**:
|
|
```
|
|
GET /carrier-dashboard/bookings?page=1&limit=10&status=PENDING
|
|
```
|
|
|
|
**Response** (200 OK):
|
|
```json
|
|
{
|
|
"data": [
|
|
{
|
|
"id": "booking-uuid",
|
|
"origin": "Rotterdam",
|
|
"destination": "New York",
|
|
"status": "PENDING",
|
|
"priceUsd": 1500,
|
|
"priceEur": 1350,
|
|
"primaryCurrency": "USD",
|
|
"requestedAt": "2025-12-04T08:00:00Z",
|
|
"carrierViewedAt": null,
|
|
"documentsCount": 3,
|
|
"volumeCBM": 25.5,
|
|
"weightKG": 12000,
|
|
"palletCount": 10,
|
|
"transitDays": 15,
|
|
"containerType": "40HC"
|
|
}
|
|
],
|
|
"total": 50,
|
|
"page": 1,
|
|
"limit": 10
|
|
}
|
|
```
|
|
|
|
**Errors**:
|
|
- `401 Unauthorized`: Invalid or expired token
|
|
- `404 Not Found`: Carrier not found
|
|
|
|
---
|
|
|
|
#### 8. Get Booking Details
|
|
|
|
**Endpoint**: `GET /carrier-dashboard/bookings/:id`
|
|
|
|
**Description**: Retrieve detailed information about a specific booking.
|
|
|
|
**Headers**:
|
|
```
|
|
Authorization: Bearer <access_token>
|
|
```
|
|
|
|
**Path Parameters**:
|
|
- `id` (string, required): Booking ID
|
|
|
|
**Response** (200 OK):
|
|
```json
|
|
{
|
|
"id": "booking-uuid",
|
|
"carrierName": "Transport Express",
|
|
"carrierEmail": "carrier@example.com",
|
|
"origin": "Rotterdam",
|
|
"destination": "New York",
|
|
"volumeCBM": 25.5,
|
|
"weightKG": 12000,
|
|
"palletCount": 10,
|
|
"priceUSD": 1500,
|
|
"priceEUR": 1350,
|
|
"primaryCurrency": "USD",
|
|
"transitDays": 15,
|
|
"containerType": "40HC",
|
|
"status": "PENDING",
|
|
"documents": [
|
|
{
|
|
"id": "doc-uuid",
|
|
"fileName": "invoice.pdf",
|
|
"type": "INVOICE",
|
|
"url": "https://storage.example.com/doc.pdf",
|
|
"uploadedAt": "2025-12-03T10:00:00Z"
|
|
}
|
|
],
|
|
"confirmationToken": "token-123",
|
|
"requestedAt": "2025-12-04T08:00:00Z",
|
|
"respondedAt": null,
|
|
"notes": "Urgent shipment",
|
|
"rejectionReason": null,
|
|
"carrierViewedAt": "2025-12-04T10:15:00Z",
|
|
"carrierAcceptedAt": null,
|
|
"carrierRejectedAt": null,
|
|
"carrierRejectionReason": null,
|
|
"carrierNotes": null,
|
|
"createdAt": "2025-12-04T08:00:00Z",
|
|
"updatedAt": "2025-12-04T10:15:00Z"
|
|
}
|
|
```
|
|
|
|
**Errors**:
|
|
- `401 Unauthorized`: Invalid or expired token
|
|
- `403 Forbidden`: Access denied to this booking
|
|
- `404 Not Found`: Booking not found
|
|
|
|
---
|
|
|
|
### Booking Management
|
|
|
|
#### 9. Accept Booking
|
|
|
|
**Endpoint**: `POST /carrier-dashboard/bookings/:id/accept`
|
|
|
|
**Description**: Accept a booking request.
|
|
|
|
**Headers**:
|
|
```
|
|
Authorization: Bearer <access_token>
|
|
```
|
|
|
|
**Path Parameters**:
|
|
- `id` (string, required): Booking ID
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"notes": "Ready to proceed. Pickup scheduled for Dec 5th."
|
|
}
|
|
```
|
|
|
|
**Response** (200 OK):
|
|
```json
|
|
{
|
|
"message": "Booking accepted successfully"
|
|
}
|
|
```
|
|
|
|
**Errors**:
|
|
- `401 Unauthorized`: Invalid or expired token
|
|
- `403 Forbidden`: Access denied to this booking
|
|
- `404 Not Found`: Booking not found
|
|
- `400 Bad Request`: Booking cannot be accepted (wrong status)
|
|
|
|
---
|
|
|
|
#### 10. Reject Booking
|
|
|
|
**Endpoint**: `POST /carrier-dashboard/bookings/:id/reject`
|
|
|
|
**Description**: Reject a booking request with a reason.
|
|
|
|
**Headers**:
|
|
```
|
|
Authorization: Bearer <access_token>
|
|
```
|
|
|
|
**Path Parameters**:
|
|
- `id` (string, required): Booking ID
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"reason": "CAPACITY_NOT_AVAILABLE",
|
|
"notes": "Sorry, we don't have capacity for this shipment at the moment."
|
|
}
|
|
```
|
|
|
|
**Response** (200 OK):
|
|
```json
|
|
{
|
|
"message": "Booking rejected successfully"
|
|
}
|
|
```
|
|
|
|
**Errors**:
|
|
- `401 Unauthorized`: Invalid or expired token
|
|
- `403 Forbidden`: Access denied to this booking
|
|
- `404 Not Found`: Booking not found
|
|
- `400 Bad Request`: Rejection reason required
|
|
- `400 Bad Request`: Booking cannot be rejected (wrong status)
|
|
|
|
---
|
|
|
|
### Document Management
|
|
|
|
#### 11. Download Document
|
|
|
|
**Endpoint**: `GET /carrier-dashboard/bookings/:bookingId/documents/:documentId/download`
|
|
|
|
**Description**: Download a document associated with a booking.
|
|
|
|
**Headers**:
|
|
```
|
|
Authorization: Bearer <access_token>
|
|
```
|
|
|
|
**Path Parameters**:
|
|
- `bookingId` (string, required): Booking ID
|
|
- `documentId` (string, required): Document ID
|
|
|
|
**Response** (200 OK):
|
|
```json
|
|
{
|
|
"document": {
|
|
"id": "doc-uuid",
|
|
"fileName": "invoice.pdf",
|
|
"type": "INVOICE",
|
|
"url": "https://storage.example.com/doc.pdf",
|
|
"size": 245678,
|
|
"mimeType": "application/pdf",
|
|
"uploadedAt": "2025-12-03T10:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Errors**:
|
|
- `401 Unauthorized`: Invalid or expired token
|
|
- `403 Forbidden`: Access denied to this document
|
|
- `404 Not Found`: Document or booking not found
|
|
|
|
---
|
|
|
|
## Data Models
|
|
|
|
### Carrier Profile
|
|
|
|
```typescript
|
|
interface CarrierProfile {
|
|
id: string;
|
|
userId: string;
|
|
organizationId: string;
|
|
companyName: string;
|
|
email: string;
|
|
phone?: string;
|
|
website?: string;
|
|
city?: string;
|
|
country?: string;
|
|
isVerified: boolean;
|
|
isActive: boolean;
|
|
totalBookingsAccepted: number;
|
|
totalBookingsRejected: number;
|
|
acceptanceRate: number;
|
|
totalRevenueUsd: number;
|
|
totalRevenueEur: number;
|
|
preferredCurrency: 'USD' | 'EUR';
|
|
lastLoginAt?: Date;
|
|
}
|
|
```
|
|
|
|
### Booking
|
|
|
|
```typescript
|
|
interface Booking {
|
|
id: string;
|
|
carrierId: string;
|
|
carrierName: string;
|
|
carrierEmail: string;
|
|
origin: string;
|
|
destination: string;
|
|
volumeCBM: number;
|
|
weightKG: number;
|
|
palletCount: number;
|
|
priceUSD: number;
|
|
priceEUR: number;
|
|
primaryCurrency: 'USD' | 'EUR';
|
|
transitDays: number;
|
|
containerType: string;
|
|
status: 'PENDING' | 'ACCEPTED' | 'REJECTED' | 'CANCELLED';
|
|
documents: Document[];
|
|
confirmationToken: string;
|
|
requestedAt: Date;
|
|
respondedAt?: Date;
|
|
notes?: string;
|
|
rejectionReason?: string;
|
|
carrierViewedAt?: Date;
|
|
carrierAcceptedAt?: Date;
|
|
carrierRejectedAt?: Date;
|
|
carrierRejectionReason?: string;
|
|
carrierNotes?: string;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
}
|
|
```
|
|
|
|
### Document
|
|
|
|
```typescript
|
|
interface Document {
|
|
id: string;
|
|
fileName: string;
|
|
type: 'INVOICE' | 'PACKING_LIST' | 'CERTIFICATE' | 'OTHER';
|
|
url: string;
|
|
size?: number;
|
|
mimeType?: string;
|
|
uploadedAt: Date;
|
|
}
|
|
```
|
|
|
|
### Activity
|
|
|
|
```typescript
|
|
interface CarrierActivity {
|
|
id: string;
|
|
carrierId: string;
|
|
bookingId?: string;
|
|
activityType: 'BOOKING_ACCEPTED' | 'BOOKING_REJECTED' | 'DOCUMENT_DOWNLOADED' | 'PROFILE_UPDATED';
|
|
description: string;
|
|
metadata?: Record<string, any>;
|
|
createdAt: Date;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Error Handling
|
|
|
|
### Error Response Format
|
|
|
|
All error responses follow this structure:
|
|
|
|
```json
|
|
{
|
|
"statusCode": 400,
|
|
"message": "Validation failed",
|
|
"error": "Bad Request",
|
|
"timestamp": "2025-12-04T10:30:00Z",
|
|
"path": "/api/v1/carrier-auth/login"
|
|
}
|
|
```
|
|
|
|
### Common HTTP Status Codes
|
|
|
|
- `200 OK`: Request successful
|
|
- `201 Created`: Resource created successfully
|
|
- `400 Bad Request`: Validation error or invalid request
|
|
- `401 Unauthorized`: Authentication required or invalid credentials
|
|
- `403 Forbidden`: Insufficient permissions
|
|
- `404 Not Found`: Resource not found
|
|
- `500 Internal Server Error`: Server error
|
|
|
|
---
|
|
|
|
## Examples
|
|
|
|
### Complete Authentication Flow
|
|
|
|
```bash
|
|
# 1. Login
|
|
curl -X POST http://localhost:4000/api/v1/carrier-auth/login \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"email": "carrier@example.com",
|
|
"password": "SecurePassword123!"
|
|
}'
|
|
|
|
# Response:
|
|
# {
|
|
# "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
# "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
# "carrier": { "id": "carrier-uuid", ... }
|
|
# }
|
|
|
|
# 2. Get Dashboard Stats
|
|
curl -X GET http://localhost:4000/api/v1/carrier-dashboard/stats \
|
|
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
|
|
# 3. Get Pending Bookings
|
|
curl -X GET "http://localhost:4000/api/v1/carrier-dashboard/bookings?status=PENDING&page=1&limit=10" \
|
|
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
|
|
# 4. Accept a Booking
|
|
curl -X POST http://localhost:4000/api/v1/carrier-dashboard/bookings/booking-uuid/accept \
|
|
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"notes": "Ready to proceed with shipment"
|
|
}'
|
|
```
|
|
|
|
### Using Auto-Login Token
|
|
|
|
```bash
|
|
# Verify auto-login token from email magic link
|
|
curl -X POST http://localhost:4000/api/v1/carrier-auth/verify-auto-login \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"token": "auto-login-token-from-email"
|
|
}'
|
|
```
|
|
|
|
---
|
|
|
|
## Rate Limiting
|
|
|
|
All API endpoints are rate-limited to prevent abuse:
|
|
|
|
- **Authentication endpoints**: 5 requests per minute per IP
|
|
- **Dashboard/Booking endpoints**: 30 requests per minute per user
|
|
- **Global limit**: 100 requests per minute per user
|
|
|
|
Rate limit headers are included in all responses:
|
|
|
|
```
|
|
X-RateLimit-Limit: 30
|
|
X-RateLimit-Remaining: 29
|
|
X-RateLimit-Reset: 60
|
|
```
|
|
|
|
---
|
|
|
|
## Security
|
|
|
|
### Best Practices
|
|
|
|
1. **Always use HTTPS** in production
|
|
2. **Store tokens securely** (e.g., httpOnly cookies, secure storage)
|
|
3. **Implement token refresh** before access token expires
|
|
4. **Validate all input** on client side before sending to API
|
|
5. **Handle errors gracefully** without exposing sensitive information
|
|
6. **Log out properly** by clearing all stored tokens
|
|
|
|
### CORS Configuration
|
|
|
|
The API allows requests from:
|
|
- `http://localhost:3000` (development)
|
|
- `https://your-production-domain.com` (production)
|
|
|
|
---
|
|
|
|
## Changelog
|
|
|
|
### Version 1.0 (2025-12-04)
|
|
- Initial release
|
|
- Authentication endpoints
|
|
- Dashboard endpoints
|
|
- Booking management
|
|
- Document management
|
|
- Complete carrier portal workflow
|
|
|
|
---
|
|
|
|
## Support
|
|
|
|
For API support or questions:
|
|
- **Email**: support@xpeditis.com
|
|
- **Documentation**: https://docs.xpeditis.com
|
|
- **Status Page**: https://status.xpeditis.com
|
|
|
|
---
|
|
|
|
**Document created**: 2025-12-04
|
|
**Author**: Xpeditis Development Team
|
|
**Version**: 1.0
|