openapi: 3.0.3 info: title: Truck Appointment API version: 1.1.0 description: PDA final complete uses the stable containerBookingId to lock the unique active appointment, transition it to COMPLETED and append operator history. No appointment does not block loading completion; multiple active appointments or an optimistic-lock conflict return A0409. paths: /api/platform/v1/truck-appointments: get: summary: Query truck appointments parameters: - {name: customerId, in: query, description: Filter by customer, schema: {type: string, pattern: '^[1-9][0-9]*$'}} responses: '200': description: Paged truck appointments with stable booking/container snapshots and attachment count content: application/json: schema: type: object properties: data: type: object properties: records: {type: array, items: {$ref: '#/components/schemas/DetailStableReferences'}} post: summary: Create a truck appointment linked to a released ocean booking requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/SaveRequest'} responses: '200': {description: Created appointment} /api/platform/v1/truck-appointments/{id}: parameters: [{$ref: '#/components/parameters/AppointmentId'}] get: summary: Query a truck appointment responses: '200': {description: Appointment detail, content: {application/json: {schema: {$ref: '#/components/schemas/DetailStableReferences'}}}} put: summary: Update an editable truck appointment with optimistic locking requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/SaveRequest'} responses: '200': {description: Updated appointment} /api/platform/v1/truck-appointments/{id}/{action}: parameters: - {$ref: '#/components/parameters/AppointmentId'} - {$ref: '#/components/parameters/Action'} - {$ref: '#/components/parameters/IdempotencyKey'} - {$ref: '#/components/parameters/RequestHash'} post: summary: Apply an idempotent appointment lifecycle action description: ARRIVE, LOAD and DEPART persist their actual event timestamps atomically with the status transition. requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/ActionRequest'} responses: '200': {description: Updated or replayed appointment} /api/platform/v1/truck-appointments/{id}/reschedule: parameters: - {$ref: '#/components/parameters/AppointmentId'} - {$ref: '#/components/parameters/IdempotencyKey'} - {$ref: '#/components/parameters/RequestHash'} - name: plannedLoadingAt in: query required: true schema: {type: string, format: date-time} post: summary: Idempotently reschedule an appointment requestBody: required: true content: application/json: schema: {$ref: '#/components/schemas/ActionRequest'} responses: '200': {description: Rescheduled or replayed appointment} /api/platform/v1/truck-appointments/{id}/history: parameters: [{$ref: '#/components/parameters/AppointmentId'}] get: summary: Query immutable appointment status history responses: '200': {description: History entries including action, from/to status, operator, reason and time} /api/platform/v1/truck-appointments/{id}/attachments: parameters: [{$ref: '#/components/parameters/AppointmentId'}] get: summary: Query attachment references owned by the appointment description: 'Requires platform:firstleg:truck-appointment:list.' responses: '200': {description: Attachment references, content: {application/json: {schema: {type: array, items: {$ref: '#/components/schemas/Attachment'}}}}} post: summary: Reference a file already owned by the appointment customer and business scope description: 'Requires platform:firstleg:truck-appointment:edit. The file must belong to customerId + TRUCK_APPOINTMENT_ATTACHMENT + appointmentId.' requestBody: {required: true, content: {application/json: {schema: {$ref: '#/components/schemas/AttachmentRequest'}}}} responses: '200': {description: Created attachment reference, content: {application/json: {schema: {$ref: '#/components/schemas/Attachment'}}}} /api/platform/v1/truck-appointments/{id}/attachments/upload: parameters: [{$ref: '#/components/parameters/AppointmentId'}] post: summary: Upload and reference a truck appointment attachment description: 'Requires platform:firstleg:truck-appointment:edit.' requestBody: required: true content: multipart/form-data: schema: type: object required: [file] properties: attachmentType: {type: string, maxLength: 64} file: {type: string, format: binary} responses: '200': {description: Uploaded attachment reference} /api/platform/v1/truck-appointments/{id}/attachments/{attachmentId}: parameters: - {$ref: '#/components/parameters/AppointmentId'} - {$ref: '#/components/parameters/AttachmentId'} delete: summary: Delete an attachment reference owned by the appointment description: 'Requires platform:firstleg:truck-appointment:edit. Deletes the appointment reference only; the File object is retained.' responses: '200': {description: Reference deleted} components: parameters: AppointmentId: {name: id, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} AttachmentId: {name: attachmentId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}} Action: name: action in: path required: true schema: type: string enum: [SUBMIT, DISPATCH, PICKUP, ARRIVE, LOAD, DEPART, COMPLETE, EXCEPTION, CANCEL] IdempotencyKey: {name: Idempotency-Key, in: header, required: true, schema: {type: string, maxLength: 128}} RequestHash: {name: X-Request-Hash, in: header, required: false, schema: {type: string, pattern: '^[0-9a-fA-F]{64}$'}} schemas: AttachmentRequest: type: object required: [fileId] properties: fileId: {type: integer, format: int64, minimum: 1} attachmentType: {type: string, maxLength: 64} fileName: {type: string, maxLength: 255, description: Ignored; authoritative metadata comes from File service} fileHash: {type: string, maxLength: 128, description: Ignored; authoritative metadata comes from File service} Attachment: type: object properties: id: {type: integer, format: int64} appointmentId: {type: integer, format: int64} fileId: {type: integer, format: int64} attachmentType: {type: string} fileName: {type: string} fileHash: {type: string} visibility: {type: string, enum: [PLATFORM]} uploadedBy: {type: integer, format: int64} uploadedAt: {type: string, format: date-time} ActionRequest: type: object required: [expectedVersion] properties: expectedVersion: {type: integer, minimum: 0} reason: {type: string, maxLength: 500} SaveRequest: type: object required: [oceanBookingId, containerBookingId, plannedLoadingAt, expectedVersion] properties: oceanBookingId: {type: integer, format: int64, minimum: 1} containerBookingId: {type: integer, format: int64, minimum: 1} loadingPlanId: {type: integer, format: int64, minimum: 1, nullable: true, description: Optional consistency hint; the server resolves and freezes the current loadingPlanId from containerBookingId and rejects a mismatch.} carrierId: {type: integer, format: int64, minimum: 1, nullable: true, description: Optional ACTIVE Warehouse supplier ID validated as TRUCKING.} carrierName: {type: string, maxLength: 128, nullable: true, description: Optional consistency hint only; the persisted snapshot comes from Warehouse and cannot be supplied without carrierId.} plannedLoadingAt: {type: string, format: date-time} expectedVersion: {type: integer, minimum: 0} DetailStableReferences: type: object description: Stable IDs plus server-resolved saved resource names returned by appointment detail. properties: oceanBookingId: {type: integer, format: int64} oceanBookingNo: {type: string} containerBookingId: {type: integer, format: int64} cabinetNo: {type: string} containerNo: {type: string, nullable: true} loadingPlanId: {type: integer, format: int64} warehouseId: {type: integer, format: int64} warehouseName: {type: string} customerId: {type: integer, format: int64} customerName: {type: string, description: Frozen from the associated ocean booking; request input cannot override it} shippingCompanyId: {type: integer, format: int64} shippingCompanyName: {type: string} bookingAgentId: {type: integer, format: int64} bookingAgentName: {type: string} carrierId: {type: integer, format: int64} carrierName: {type: string} attachmentCount: {type: integer, format: int64, minimum: 0, description: Number of attachment references owned by this appointment}