2026-08-12 17:29:30 +08:00
openapi : 3.0.3
info : {title : Phase 3 Ocean Booking API, version : 1.1.0 }
paths :
2026-08-14 18:50:07 +08:00
/api/platform/v1/ocean-bookings/channels :
get :
summary : List logistics channels from the current ACTIVE price book for an origin warehouse
2026-08-15 09:47:17 +08:00
description : 'Requires platform:firstleg:ocean-booking:list. Returns only channels with an ACTIVE price row; the selected channel name is frozen server-side when the offline-confirmed booking is registered.'
2026-08-14 18:50:07 +08:00
parameters :
- {name : originWarehouseId, in : query, required : true, schema : {type : integer, format : int64, minimum : 1 }}
responses : {'200' : {description : Available channel options, content : {application/json : {schema : {type : array, items : {$ref : '#/components/schemas/ChannelOption' }}}}}}
2026-08-12 17:29:30 +08:00
/api/platform/v1/ocean-bookings :
2026-08-14 20:59:03 +08:00
get :
summary : List only real confirmed booking records
2026-08-15 09:47:17 +08:00
description : 'Requires platform:firstleg:ocean-booking:list. The operational list exposes only CONFIRMED, CHANGE_PENDING, RELEASED and CANCELLED bookings; DRAFT is internal to the create transaction and legacy SUBMITTED/FAILED values are invalid.'
2026-08-14 20:59:03 +08:00
parameters :
- {name : customerId, in : query, schema : {type : integer, format : int64, minimum : 1 }}
- {name : status, in : query, schema : {type : string, enum : [ CONFIRMED, CHANGE_PENDING, RELEASED, CANCELLED]}}
- {name : keyword, in : query, schema : {type : string, maxLength : 128 }}
- {name : page, in : query, schema : {type : integer, minimum : 1, default : 1 }}
- {name : size, in : query, schema : {type : integer, minimum : 1, maximum : 200, default : 20 }}
responses : {'200' : {description : Operational real booking page}}
2026-08-12 17:29:30 +08:00
post :
2026-08-15 09:47:17 +08:00
summary : Register an offline-confirmed ocean booking with complete transport snapshots
2026-08-17 13:24:48 +08:00
description : 'Requires platform:firstleg:ocean-booking:create. Registers every real container unit atomically, creates one stable fl_container_booking plus one requirement/allocation per container, and persists the booking directly as CONFIRMED. RELEASED remains a separate later action.'
2026-08-12 17:29:30 +08:00
parameters : [ {$ref : '#/components/parameters/IdempotencyKey' }]
requestBody : {required : true, content : {application/json : {schema : {$ref : '#/components/schemas/CreateRequest' }}}}
2026-08-17 13:24:48 +08:00
responses : {'200' : {description : CONFIRMED real booking detail, content : {application/json : {schema : {$ref : '#/components/schemas/Detail' }}}}}
2026-08-12 17:29:30 +08:00
/api/platform/v1/ocean-bookings/{oceanBookingId}/release:
post :
2026-08-15 09:47:17 +08:00
summary : Release an already registered booking and synchronize associated cabinet transport snapshots
description : 'The transport fields were registered at create time. The request must repeat the same bill of lading, vessel, voyage, ETD and ETA values; mismatches are rejected. This action does not create cabinets, change channels, replace cabinetNo, or recreate loading plans.'
2026-08-12 17:29:30 +08:00
parameters : [ {$ref : '#/components/parameters/OceanBookingId' }, {$ref : '#/components/parameters/IdempotencyKey' }]
requestBody : {required : true, content : {application/json : {schema : {$ref : '#/components/schemas/ReleaseRequest' }}}}
responses : {'200' : {description : RELEASED detail}}
2026-08-17 13:24:48 +08:00
/api/platform/v1/ocean-bookings/{oceanBookingId}:
get :
summary : Get an ocean booking with its real container units
description : Requires platform:firstleg:ocean-booking:list.
parameters : [ {$ref : '#/components/parameters/OceanBookingId' }]
responses : {'200' : {description : Booking detail, content : {application/json : {schema : {$ref : '#/components/schemas/Detail' }}}}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/cancel:
post :
summary : Cancel a confirmed or change-pending real booking
description : Requires platform:firstleg:ocean-booking:cancel. A non-blank reason is required; RELEASED bookings are irreversible.
parameters : [ {$ref : '#/components/parameters/OceanBookingId' }, {$ref : '#/components/parameters/IdempotencyKey' }]
requestBody : {required : true, content : {application/json : {schema : {$ref : '#/components/schemas/ActionRequest' }}}}
responses : {'200' : {description : CANCELLED detail}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/change-request:
post :
summary : Request a voyage change for a confirmed booking
description : Requires platform:firstleg:ocean-booking:change. Current transport snapshots remain active until change-confirm succeeds.
parameters : [ {$ref : '#/components/parameters/OceanBookingId' }, {$ref : '#/components/parameters/IdempotencyKey' }]
requestBody : {required : true, content : {application/json : {schema : {$ref : '#/components/schemas/ChangeRequest' }}}}
responses : {'200' : {description : CHANGE_PENDING detail}}
/api/platform/v1/ocean-bookings/{oceanBookingId}/change-confirm:
post :
summary : Confirm a pending voyage change
description : Requires platform:firstleg:ocean-booking:change.
parameters : [ {$ref : '#/components/parameters/OceanBookingId' }, {$ref : '#/components/parameters/IdempotencyKey' }]
requestBody : {required : true, content : {application/json : {schema : {$ref : '#/components/schemas/ActionRequest' }}}}
responses : {'200' : {description : CONFIRMED detail with the pending voyage snapshots applied}}
2026-08-12 17:29:30 +08:00
components :
parameters :
OceanBookingId : {name : oceanBookingId, in : path, required : true, schema : {type : integer, format : int64, minimum : 1 }}
IdempotencyKey : {name : Idempotency-Key, in : header, required : true, schema : {type : string, maxLength : 128 }}
schemas :
CreateRequest :
type : object
required : [ expectedVersion, data]
properties :
expectedVersion : {type : integer, enum : [ 0 ] }
data : {$ref : '#/components/schemas/DraftData' }
DraftData :
type : object
2026-08-17 13:24:48 +08:00
required : [ channelCode, warehouseId, billOfLadingNo, vesselName, voyageNo, serviceTerm, freightTerm, estimatedSailingAt, estimatedArrivalAt, parties, containers]
2026-08-12 17:29:30 +08:00
properties :
2026-08-14 19:59:26 +08:00
customerId : {type : integer, format : int64, minimum : 1, nullable : true, description : Optional customer snapshot; must be supplied together with customerName.}
customerName : {type : string, maxLength : 128, nullable : true, description : Optional customer snapshot; must be supplied together with customerId.}
2026-08-14 18:50:07 +08:00
channelCode : {type : string, maxLength : 64, description : Selected from GET /api/platform/v1/ocean-bookings/channels; server validates it against the current ACTIVE price book and freezes channelName.}
2026-08-17 13:24:48 +08:00
warehouseId : {type : integer, format : int64, minimum : 1, description : ACTIVE domestic transit warehouse ID; warehouseName is resolved and frozen server-side.}
shippingCompanyId : {type : integer, format : int64, minimum : 1, nullable : true, description : Optional shipping-company master-data ID; the carrier is independent from the logistics channel and booking-agent Supplier.}
shippingCompanyName : {type : string, maxLength : 128, nullable : true, description : Optional shipping-company name snapshot.}
2026-08-17 14:46:44 +08:00
bookingAgentId : {type : integer, format : int64, minimum : 1, nullable : true, description : Optional ACTIVE Warehouse supplier ID validated only as category BOOKING; booking suppliers do not use domestic/international scope.}
2026-08-15 10:35:54 +08:00
bookingAgentName : {type : string, maxLength : 128, nullable : true, description : Optional consistency hint only; the persisted snapshot always comes from Warehouse and cannot be supplied without bookingAgentId.}
2026-08-14 19:59:26 +08:00
serviceTerm : {type : string, maxLength : 32, description : Required free-text Service Term snapshot, for example CY-CY.}
freightTerm : {type : string, maxLength : 32, description : Required free-text Freight Term snapshot, for example NINGBO.}
2026-08-15 09:47:17 +08:00
billOfLadingNo : {type : string, maxLength : 128, description : Required offline-confirmed bill of lading snapshot.}
vesselName : {type : string, maxLength : 128, description : Required offline-confirmed vessel snapshot.}
voyageNo : {type : string, maxLength : 64, description : Required offline-confirmed voyage snapshot.}
2026-08-17 13:24:48 +08:00
departurePortCode : {type : string, maxLength : 32, nullable : true }
departurePortName : {type : string, maxLength : 128, nullable : true }
destinationPortCode : {type : string, maxLength : 32, nullable : true }
destinationPortName : {type : string, maxLength : 128, nullable : true }
placeOfDelivery : {type : string, maxLength : 128, nullable : true }
2026-08-15 09:47:17 +08:00
estimatedSailingAt : {type : string, format : date-time, description : Required offline-confirmed ETD snapshot.}
estimatedArrivalAt : {type : string, format : date-time, description : Required offline-confirmed ETA snapshot; cannot be earlier than ETD.}
2026-08-17 13:24:48 +08:00
remark : {type : string, maxLength : 1000, nullable : true }
parties : {type : array, minItems : 3, maxItems : 3, items : {$ref : '#/components/schemas/PartyRequest' }}
containers : {type : array, minItems : 1, maxItems : 20, items : {$ref : '#/components/schemas/ContainerRequest' }}
PartyRequest :
2026-08-12 17:29:30 +08:00
type : object
2026-08-17 13:24:48 +08:00
required : [ partyType, partyName]
2026-08-12 17:29:30 +08:00
properties :
2026-08-17 13:24:48 +08:00
partyType : {type : string, enum : [ SHIPPER, CONSIGNEE, NOTIFY_PARTY]}
partyName : {type : string, maxLength : 255 }
address : {type : string, maxLength : 1000, nullable : true }
contactName : {type : string, maxLength : 128, nullable : true }
contactPhone : {type : string, maxLength : 64, nullable : true }
ContainerRequest :
type : object
required : [ containerNo, containerType, containerKind, capacityCbm, maxWeightKg]
additionalProperties : false
properties :
containerNo : {type : string, minLength : 1, maxLength : 64, description : Required real container number; unique across all ACTIVE and unmerged container records.}
2026-08-17 12:31:24 +08:00
containerType : {type : string, enum : [20GP, 40GP, 40HC, 40NR, 45HC], description : Platform-controlled container type}
2026-08-12 17:29:30 +08:00
containerKind : {type : string, maxLength : 32 }
2026-08-17 12:31:24 +08:00
capacityCbm : {type : number, exclusiveMinimum : true, minimum : 0, description : Must match the fixed platform specification for containerType}
maxWeightKg : {type : number, exclusiveMinimum : true, minimum : 0, description : Must match the fixed platform specification for containerType}
2026-08-17 13:24:48 +08:00
Detail :
type : object
required : [ oceanBooking, parties, containers, histories]
properties :
oceanBooking : {type : object, description : Ocean booking aggregate and frozen transport snapshots.}
parties : {type : array, items : {type : object}}
containers : {type : array, items : {$ref : '#/components/schemas/RealContainer' }}
histories : {type : array, items : {type : object}}
RealContainer :
type : object
required : [ containerBookingId, cabinetNo, containerNo, requirementId, allocationId, containerType, containerKind, capacityCbm, maxWeightKg, warehouseId, warehouseName, channelCode, channelName]
properties :
containerBookingId : {type : integer, format : int64, minimum : 1, description : Current ACTIVE operational container identity.}
cabinetNo : {type : string, maxLength : 64, description : Stable server-generated internal cabinet number.}
containerNo : {type : string, maxLength : 64, description : Unique real container number.}
requirementId : {type : integer, format : int64, minimum : 1 }
allocationId : {type : integer, format : int64, minimum : 1 }
containerType : {type : string, enum : [ 20GP, 40GP, 40HC, 40NR, 45HC]}
containerKind : {type : string, maxLength : 32 }
capacityCbm : {type : number}
maxWeightKg : {type : number}
warehouseId : {type : integer, format : int64, minimum : 1 }
warehouseName : {type : string, maxLength : 128 }
channelCode : {type : string, maxLength : 64 }
channelName : {type : string, maxLength : 128 }
customerId : {type : integer, format : int64, minimum : 1, nullable : true }
customerName : {type : string, maxLength : 128, nullable : true }
2026-08-14 18:50:07 +08:00
ChannelOption :
type : object
required : [ priceBookVersionId, channelCode, channelName, priceModel, pricingTaxModes]
properties :
priceBookVersionId : {type : integer, format : int64}
channelCode : {type : string, maxLength : 64 }
channelName : {type : string, maxLength : 128 }
priceModel : {type : string}
pricingTaxModes : {type : array, items : {type : string}}
2026-08-12 17:29:30 +08:00
ActionRequest :
type : object
required : [ expectedVersion]
2026-08-17 13:24:48 +08:00
properties : {expectedVersion : {type : integer, minimum : 0}, reason : {type : string, maxLength : 1000, nullable : true }}
ChangeRequest :
type : object
required : [ expectedVersion, vesselName, voyageNo, reason]
properties :
expectedVersion : {type : integer, minimum : 0 }
vesselName : {type : string, maxLength : 128 }
voyageNo : {type : string, maxLength : 64 }
estimatedSailingAt : {type : string, format : date-time, nullable : true }
estimatedArrivalAt : {type : string, format : date-time, nullable : true }
reason : {type : string, maxLength : 1000 }
2026-08-12 17:29:30 +08:00
ReleaseRequest :
type : object
required : [ expectedVersion, billOfLadingNo, vesselName, voyageNo, estimatedSailingAt, estimatedArrivalAt]
properties :
expectedVersion : {type : integer, minimum : 0 }
billOfLadingNo : {type : string, maxLength : 128 }
vesselName : {type : string, maxLength : 128 }
voyageNo : {type : string, maxLength : 64 }
estimatedSailingAt : {type : string, format : date-time}
estimatedArrivalAt : {type : string, format : date-time}
reason : {type : string, maxLength : 1000 }