openapi: 3.1.0
info:
  title: Aquiline Tracking API
  version: v3
  summary: Shipment visibility and delivery operations for partner integrations.
  description: |
    Aquiline Tracking API enables partners to create shipments, calculate delivery options,
    validate addresses, retrieve tracking visibility, schedule pickups, and subscribe to
    webhook events across operational workflows.
servers:
  - url: https://api.aquiline-tracking.com/v3
    description: Production
tags:
  - name: Rates
    description: Delivery pricing, service options, and estimated delivery windows.
  - name: Addresses
    description: Address validation and deliverability checks.
  - name: Shipments
    description: Shipment creation, retrieval, and cancellation.
  - name: Tracking
    description: Shipment status, milestone visibility, and event timelines.
  - name: Pickups
    description: Pickup booking and operational pickup management.
  - name: Webhooks
    description: Event subscriptions for shipment and pickup updates.
x-tagGroups:
  - name: Shipment Flow
    tags:
      - Addresses
      - Rates
      - Shipments
      - Tracking
  - name: Operations
    tags:
      - Pickups
      - Webhooks
security:
  - ApiKeyAuth: []
paths:
  /addresses/validate:
    post:
      tags:
        - Addresses
      summary: Validate address
      operationId: validateAddress
      description: Normalize destination data and confirm whether the address is serviceable.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddressValidationRequest'
      responses:
        '200':
          description: Address validation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AddressValidationResponse'
  /rates/quote:
    post:
      tags:
        - Rates
      summary: Calculate delivery quote
      operationId: createRateQuote
      description: Return available service levels, delivery windows, and pricing for a shipment request.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RateQuoteRequest'
            examples:
              standard:
                value:
                  externalOrderId: order_100045
                  origin:
                    countryCode: AE
                    city: Dubai
                    postalCode: "00000"
                    addressLine1: Warehouse 14, Al Quoz
                  destination:
                    countryCode: AE
                    city: Dubai
                    postalCode: "00000"
                    addressLine1: Dubai Marina, Cluster A
                  parcels:
                    - weightKg: 1.2
                      lengthCm: 25
                      widthCm: 18
                      heightCm: 12
      responses:
        '200':
          description: Quote results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateQuoteResponse'
  /shipments:
    post:
      tags:
        - Shipments
      summary: Create shipment
      operationId: createShipment
      description: Create a shipment and return Aquiline shipment identifiers, tracking number, and label metadata.
      parameters:
        - in: header
          name: Idempotency-Key
          required: true
          description: Unique key used to safely retry shipment creation requests.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateShipmentRequest'
      responses:
        '201':
          description: Shipment created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Shipment'
  /shipments/{shipmentId}:
    get:
      tags:
        - Shipments
      summary: Retrieve shipment
      operationId: getShipment
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
      responses:
        '200':
          description: Shipment details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Shipment'
  /shipments/{shipmentId}/cancel:
    post:
      tags:
        - Shipments
      summary: Cancel shipment
      operationId: cancelShipment
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
      responses:
        '200':
          description: Shipment cancelled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Shipment'
  /tracking/{trackingNumber}:
    get:
      tags:
        - Tracking
      summary: Retrieve tracking status
      operationId: getTrackingStatus
      parameters:
        - $ref: '#/components/parameters/TrackingNumber'
      responses:
        '200':
          description: Tracking status response
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/TrackingResponse'
                  - $ref: '#/components/schemas/TrackingFailureResponse'
  /pickups:
    post:
      tags:
        - Pickups
      summary: Create pickup
      operationId: createPickup
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePickupRequest'
      responses:
        '201':
          description: Pickup created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pickup'
  /webhooks/subscriptions:
    post:
      tags:
        - Webhooks
      summary: Create webhook subscription
      operationId: createWebhookSubscription
      parameters:
        - $ref: '#/components/parameters/PartnerIdHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWebhookSubscriptionRequest'
      responses:
        '200':
          description: Webhook subscription created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateWebhookSubscriptionResponse'
    get:
      tags:
        - Webhooks
      summary: List webhook subscriptions
      operationId: listWebhookSubscriptions
      parameters:
        - $ref: '#/components/parameters/PartnerIdHeader'
      responses:
        '200':
          description: Webhook subscriptions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListWebhookSubscriptionsResponse'
  /webhooks/subscriptions/{subscriptionId}:
    delete:
      tags:
        - Webhooks
      summary: Deactivate webhook subscription
      operationId: deleteWebhookSubscription
      parameters:
        - $ref: '#/components/parameters/PartnerIdHeader'
        - $ref: '#/components/parameters/WebhookSubscriptionId'
      responses:
        '200':
          description: Webhook subscription deactivated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteWebhookSubscriptionResponse'
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
  parameters:
    ShipmentId:
      name: shipmentId
      in: path
      required: true
      schema:
        type: string
      example: shp_01HRD5ZCMQ
    TrackingNumber:
      name: trackingNumber
      in: path
      required: true
      schema:
        type: string
      example: AQAA123456789YQ
    PartnerIdHeader:
      name: X-Partner-Id
      in: header
      required: true
      schema:
        type: string
      description: Partner routing identifier used for ownership and filtering. Must match the partner bound to `X-API-Key`.
      example: partner_123
    WebhookSubscriptionId:
      name: subscriptionId
      in: path
      required: true
      schema:
        type: string
      example: whs_01J2V6FQ2M9Q
  schemas:
    Money:
      type: object
      required:
        - amount
        - currency
      properties:
        amount:
          type: number
          example: 149.99
        currency:
          type: string
          example: AED
    Parcel:
      type: object
      required:
        - weightKg
        - lengthCm
        - widthCm
        - heightCm
      properties:
        weightKg:
          type: number
          example: 1.2
        lengthCm:
          type: number
          example: 25
        widthCm:
          type: number
          example: 18
        heightCm:
          type: number
          example: 12
    Address:
      type: object
      required:
        - name
        - phone
        - countryCode
        - city
        - addressLine1
      properties:
        name:
          type: string
          example: John Doe
        phone:
          type: string
          example: "+971500000002"
        countryCode:
          type: string
          example: AE
        city:
          type: string
          example: Dubai
        postalCode:
          type:
            - string
            - 'null'
          example: "00000"
        addressLine1:
          type: string
          example: Dubai Marina, Cluster A
        addressLine2:
          type:
            - string
            - 'null'
          example: Apartment 2301
    AddressLite:
      type: object
      required:
        - countryCode
        - city
        - addressLine1
      properties:
        countryCode:
          type: string
        city:
          type: string
        postalCode:
          type:
            - string
            - 'null'
        addressLine1:
          type: string
    RateQuoteRequest:
      type: object
      required:
        - origin
        - destination
        - parcels
      properties:
        externalOrderId:
          type:
            - string
            - 'null'
        origin:
          $ref: '#/components/schemas/AddressLite'
        destination:
          $ref: '#/components/schemas/AddressLite'
        parcels:
          type: array
          items:
            $ref: '#/components/schemas/Parcel'
    QuoteOption:
      type: object
      required:
        - quoteId
        - serviceLevel
        - price
        - eta
      properties:
        quoteId:
          type: string
          example: quote_01HRD4H6F0
        serviceLevel:
          type: string
          example: same_day
        price:
          $ref: '#/components/schemas/Money'
        eta:
          type: string
          format: date-time
          example: '2026-03-18T14:00:00Z'
        deliveryWindow:
          type: object
          required:
            - from
            - to
          properties:
            from:
              type: string
              format: date-time
            to:
              type: string
              format: date-time
    RateQuoteResponse:
      type: object
      required:
        - options
      properties:
        options:
          type: array
          items:
            $ref: '#/components/schemas/QuoteOption'
    AddressValidationRequest:
      type: object
      required:
        - address
      properties:
        address:
          $ref: '#/components/schemas/AddressLite'
    AddressValidationResponse:
      type: object
      required:
        - isValid
        - deliverable
        - normalizedAddress
      properties:
        isValid:
          type: boolean
          example: true
        deliverable:
          type: boolean
          example: true
        normalizedAddress:
          $ref: '#/components/schemas/AddressLite'
        warnings:
          type: array
          items:
            type: string
    CreateShipmentRequest:
      type: object
      required:
        - externalOrderId
        - serviceLevel
        - sender
        - recipient
        - parcels
      properties:
        externalOrderId:
          type: string
        quoteId:
          type:
            - string
            - 'null'
        serviceLevel:
          type: string
          example: same_day
        sender:
          $ref: '#/components/schemas/Address'
        recipient:
          $ref: '#/components/schemas/Address'
        parcels:
          type: array
          items:
            $ref: '#/components/schemas/Parcel'
        codAmount:
          $ref: '#/components/schemas/Money'
        customerNotes:
          type:
            - string
            - 'null'
    Shipment:
      type: object
      required:
        - shipmentId
        - trackingNumber
        - status
        - serviceLevel
        - sender
        - recipient
        - parcels
        - createdAt
        - updatedAt
      properties:
        shipmentId:
          type: string
          example: shp_01HRD5ZCMQ
        externalOrderId:
          type: string
          example: order_100045
        trackingNumber:
          type: string
          example: AQAA123456789YQ
        status:
          type: string
          example: created
        serviceLevel:
          type: string
          example: same_day
        labelUrl:
          type: string
          format: uri
          example: https://cdn.aquiline.com/labels/shp_01HRD5ZCMQ.pdf
        sender:
          $ref: '#/components/schemas/Address'
        recipient:
          $ref: '#/components/schemas/Address'
        parcels:
          type: array
          items:
            $ref: '#/components/schemas/Parcel'
        codAmount:
          $ref: '#/components/schemas/Money'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    TrackingEvent:
      type: object
      required:
        - content
        - time
      properties:
        content:
          type: string
          example: Package left the courier facility
        location:
          type: string
          example: ""
        time:
          type: string
          example: "2025-11-20 00:00:00"
    TrackingResponse:
      type: object
      required:
        - number
        - status
        - events
      properties:
        number:
          type: string
          example: AQAA123456789YQ
        status:
          type: string
          example: Shipping
        oriCountry:
          type: string
          example: UK
        destCountry:
          type: string
          example: GB
        events:
          type: array
          items:
            $ref: '#/components/schemas/TrackingEvent'
    TrackingFailureResponse:
      type: object
      required:
        - type
        - message
      properties:
        type:
          type: string
          example: failure
        message:
          type: string
          example: Tracking not assigned more than 15 days. Contact support.
    CreatePickupRequest:
      type: object
      required:
        - pickupAddress
        - requestedWindow
      properties:
        pickupAddress:
          $ref: '#/components/schemas/Address'
        requestedWindow:
          type: object
          required:
            - from
            - to
          properties:
            from:
              type: string
              format: date-time
            to:
              type: string
              format: date-time
        shipmentIds:
          type: array
          items:
            type: string
    Pickup:
      type: object
      required:
        - pickupId
        - status
        - requestedWindow
      properties:
        pickupId:
          type: string
          example: pck_01HRD6F80Q
        status:
          type: string
          example: scheduled
        requestedWindow:
          type: object
          required:
            - from
            - to
          properties:
            from:
              type: string
              format: date-time
            to:
              type: string
              format: date-time
    CreateWebhookSubscriptionRequest:
      type: object
      description: |
        Webhook deliveries are emitted for shipment timeline appends and for status-only transitions.
        For status-only transitions, delivery payload keeps `newEvents` empty and marks `changeType` as `status_change`.
      required:
        - webhookUrl
        - events
      properties:
        webhookUrl:
          type: string
          format: uri
          example: https://partner.example.com/aquiline/webhooks
        events:
          type: array
          items:
            type: string
            enum:
              - shipment.updated
              - shipment.in_transit
              - shipment.out_for_delivery
              - shipment.delivered
              - shipment.exception
              - pickup.updated
          example:
            - shipment.updated
            - shipment.out_for_delivery
            - shipment.delivered
          description: |
            Subscribed event types. `shipment.updated` is the canonical fallback emitted for status codes
            outside the explicit shipment mapping.
        secret:
          type:
            - string
            - 'null'
        trackingNumber:
          type:
            - string
            - 'null'
          description: Optional tracking number filter; when provided, events are sent only for this track.
          example: AQUAA0849240326YQ
    CreateWebhookSubscriptionResponse:
      type: object
      required:
        - success
        - id
      properties:
        success:
          type: boolean
          example: true
        id:
          type: string
          example: whs_01J2V6FQ2M9Q
    DeleteWebhookSubscriptionResponse:
      type: object
      required:
        - success
        - id
        - isActive
        - expiresAt
      properties:
        success:
          type: boolean
          example: true
        id:
          type: string
          example: whs_01J2V6FQ2M9Q
        isActive:
          type: boolean
          example: false
        expiresAt:
          type: string
          format: date-time
          example: '2026-06-22T13:30:00.000Z'
    ListWebhookSubscriptionsResponse:
      type: object
      required:
        - success
        - subscriptions
      properties:
        success:
          type: boolean
          example: true
        subscriptions:
          type: array
          items:
            $ref: '#/components/schemas/WebhookSubscription'
    WebhookSubscription:
      type: object
      required:
        - id
        - partnerId
        - webhookUrl
        - events
        - isActive
      properties:
        id:
          type: string
          example: whs_01HRD6ZQJ9
        partnerId:
          type: string
          example: partner_123
        webhookUrl:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
          description: Includes explicit shipment events and optional fallback `shipment.updated`.
        trackingNumber:
          type:
            - string
            - 'null'
          example: AQUAA0849240326YQ
        secret:
          type:
            - string
            - 'null'
          description: Stored value is masked in partner-facing responses.
          example: null
        isActive:
          type: boolean
          example: true
        createdAt:
          $ref: '#/components/schemas/FirestoreTimestamp'
        updatedAt:
          $ref: '#/components/schemas/FirestoreTimestamp'
    FirestoreTimestamp:
      type: object
      required:
        - _seconds
        - _nanoseconds
      properties:
        _seconds:
          type: integer
          example: 1774354295
        _nanoseconds:
          type: integer
          example: 376000000
