openapi: 3.1.0
info:
  version: 2.0.0
  title: Real Time Tracking API
  license:
    name: Proprietary
    url: https://bettermile.com/legal-notice
  description: Real Time Tracking API - Managed by Tracking Team
  contact:
    url: https://bettermile.com
    email: tracking@bettermile.com
servers:
  - url: https://tracking.bettermile.com
    description: PROD
tags:
  - name: Parcel
    description: Endpoints related to parcel information and tracking
  - name: Availability
    description: Endpoints related to parcel availability
  - name: Customization
    description: Endpoints related to parcel customizations
paths:
  /api/v2/external/parcel/{parcelIdentifier}:
    get:
      tags:
        - Parcel
      summary: Parcel
      description: >-
        The Parcel endpoint returns information regarding a given parcel. This
        broadly includes shipper and consignee address, delivery information,
        ETA timestamps and much more.
      operationId: getExternalParcel
      security:
        - bearerToken: []
      parameters:
        - $ref: '#/components/parameters/parcelIdentifierPathParam'
      responses:
        '200':
          $ref: '#/components/responses/ParcelResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        default:
          $ref: '#/components/responses/Error'
  /api/v2/external/parcel/{parcelIdentifier}/availability:
    get:
      tags:
        - Availability
      summary: Tracking Availability
      description: Checks if parcel is in delivery and real time tracking is available
      operationId: getExternalParcelAvailability
      security:
        - bearerToken: []
      parameters:
        - $ref: '#/components/parameters/parcelIdentifierPathParam'
      responses:
        '200':
          $ref: '#/components/responses/AvailabilityResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/TrackingNotAvailableError'
        default:
          $ref: '#/components/responses/Error'
  /api/v2/external/parcel/{parcelIdentifier}/customization:
    put:
      tags:
        - Customization
      summary: Customization
      description: >-
        Add customized delivery (neighbor delivery, deposit permission,
        consignee note etc.) for parcel by tenant id and parcel identifier
      operationId: putExternalParcelCustomization
      security:
        - bearerToken: []
      parameters:
        - $ref: '#/components/parameters/parcelIdentifierPathParam'
      requestBody:
        description: Actual customized delivery for parcel
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomizedDelivery'
      responses:
        '200':
          $ref: '#/components/responses/ParcelResponse'
        default:
          $ref: '#/components/responses/Error'
components:
  parameters:
    parcelIdentifierPathParam:
      in: path
      name: parcelIdentifier
      required: true
      schema:
        type: string
        description: Parcel number or tracking id
        example: '10362781851'
  responses:
    Error:
      description: Error message
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    TrackingNotAvailableError:
      description: Parcel not found or not available for real time tracking
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Authorization information is missing or invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Unauthorized'
    ParcelResponse:
      description: Parcel response
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Parcel'
    AvailabilityResponse:
      description: Parcel is in delivery and available for real time tracking
  schemas:
    CustomizedDeliveryType:
      type: string
      enum:
        - DEPOSIT_PERMISSION
        - DELIVER_TO_NEIGHBOR
        - DELIVER_TO_PARCELSHOP
        - HINT
    ParcelStatusNotDelivered:
      type: object
      required:
        - type
        - detail
      properties:
        type:
          type: string
          enum:
            - NOT_DELIVERED
        detail:
          $ref: '#/components/schemas/ParcelStatusDetailTypeNotDelivered'
    ParcelStatusBlocked:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - BLOCKED
    ParcelStatusUnknown:
      type: object
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - UNKNOWN
    ParcelStatusDetailTypeDelivered:
      type: string
      enum:
        - NORMAL
        - NEIGHBOR
        - PARCEL_SHOP
        - DEPOSIT
        - OTHER
    ParcelStatusDetailTypeNotDelivered:
      type: string
      enum:
        - NORMAL
        - TRAFFIC_PROBLEMS
        - DELIVERY_NOT_ACCEPTED
        - CONSIGNEE_ABSENT
        - ADDRESS_PROBLEM
        - OTHER
    ShipperId:
      type: string
      example: bmw
    JobType:
      type: string
      enum:
        - DELIVERY
        - PICKUP
        - COLLECTION
        - UNKNOWN
    Parcel:
      required:
        - parcelNumber
        - trackingId
        - status
        - jobType
        - supportedCustomizedDeliveryTypes
      properties:
        trackingId:
          type: string
          example: YUTKBBTN
        parcelNumber:
          type: string
          example: '10362781851'
        status:
          $ref: '#/components/schemas/ParcelStatus'
        shipperId:
          $ref: '#/components/schemas/ShipperId'
        jobType:
          $ref: '#/components/schemas/JobType'
        supportedCustomizedDeliveryTypes:
          type: array
          items:
            $ref: '#/components/schemas/CustomizedDeliveryType'
        customizedDelivery:
          $ref: '#/components/schemas/CustomizedDelivery'
        fromAddress:
          $ref: '#/components/schemas/AddressName'
        toAddress:
          $ref: '#/components/schemas/Address'
    ParcelStatus:
      oneOf:
        - $ref: '#/components/schemas/ParcelStatusDelivered'
        - $ref: '#/components/schemas/ParcelStatusNotDelivered'
        - $ref: '#/components/schemas/ParcelStatusOutForDelivery'
        - $ref: '#/components/schemas/ParcelStatusBlocked'
        - $ref: '#/components/schemas/ParcelStatusUnknown'
      discriminator:
        propertyName: type
    ParcelStatusDelivered:
      type: object
      required:
        - type
        - detail
      properties:
        type:
          type: string
          enum:
            - DELIVERED
        detail:
          $ref: '#/components/schemas/ParcelStatusDetailTypeDelivered'
        deliveredAt:
          type: string
          format: date-time
          example: '2022-01-15T14:33:25Z'
        address:
          description: Parcel shop address, present only for detail = 'TO_PARCELSHOP'
          $ref: '#/components/schemas/Address'
        name:
          format: string
          description: Neighbour name, present only for detail = 'TO_NEIGHBOR'
    ParcelStatusOutForDelivery:
      type: object
      required:
        - type
        - detail
      properties:
        type:
          type: string
          enum:
            - OUT_FOR_DELIVERY
        position:
          description: Driver's current location
          $ref: '#/components/schemas/Coordinates'
        etaTimestampMin:
          type: string
          format: date-time
          description: Earliest estimated time of arrival
          example: '2022-01-15T14:15:00Z'
        etaTimestampMax:
          type: string
          format: date-time
          description: Latest estimated time of arrival
          example: '2022-01-15T14:45:00Z'
        etaChangeFactor:
          type: string
          enum:
            - VERY_EARLY
            - VERY_DELAYED
          description: >-
            Indicates significant changes in the estimated time of arrival - 90
            mins ETA jumps for earlier or later
        remainingStops:
          type: integer
          description: Number of driver's remaining stops before delivery of the parcel
          example: 5
    AddressName:
      properties:
        name:
          type: string
          example: Max Mustermann
    Address:
      allOf:
        - $ref: '#/components/schemas/AddressName'
        - type: object
          properties:
            street:
              type: string
              example: GLS Germany-Straße
            number:
              type: string
              example: 1-7
            postcode:
              type: string
              example: 36286
            city:
              type: string
              example: Neuenstein
            country:
              type: string
              minLength: 2
              maxLength: 2
              description: Two digit country code of format ISO 3166-1 alpha-2
              example: DE
            coordinates:
              $ref: '#/components/schemas/Coordinates'
    Coordinates:
      required:
        - lat
        - lng
      properties:
        lat:
          type: number
          description: Geographic latitude coordinate
          example: 50.9098881
        lng:
          type: number
          description: Geographic longitude coordinate
          example: 9.5806327
    CustomizedDelivery:
      oneOf:
        - $ref: '#/components/schemas/DepositPermission'
        - $ref: '#/components/schemas/DeliverToNeighbor'
        - $ref: '#/components/schemas/DeliverToParcelShop'
        - $ref: '#/components/schemas/DeliveryNote'
      discriminator:
        propertyName: type
    DepositPermission:
      required:
        - type
        - createdAt
        - description
        - location
      properties:
        type:
          type: string
          enum:
            - DEPOSIT_PERMISSION
        createdAt:
          type: string
          format: date-time
          example: '2022-01-15T14:33:25Z'
        location:
          type: string
          example: Carport
        description:
          type: string
          example: Hinter der Tonne
    DeliverToNeighbor:
      required:
        - type
        - createdAt
        - description
        - fullName
      properties:
        type:
          type: string
          enum:
            - DELIVER_TO_NEIGHBOR
        createdAt:
          type: string
          format: date-time
          example: '2022-01-15T14:33:25Z'
        fullName:
          type: string
          example: Max Mustermann
        description:
          type: string
          example: Vorderhaus 2. OG
    DeliverToParcelShop:
      required:
        - type
        - createdAt
        - description
        - parcelShopId
        - parcelShopName
        - parcelShopAddress
      properties:
        type:
          type: string
          enum:
            - DELIVER_TO_PARCELSHOP
        createdAt:
          type: string
          format: date-time
          example: '2022-01-15T14:33:25Z'
        parcelShopId:
          type: string
          example: '616011346'
        parcelShopName:
          type: string
          example: Späti 24
        parcelShopAddress:
          $ref: '#/components/schemas/ParcelShopAddress'
    ParcelShopAddress:
      required:
        - street
        - houseNumber
        - zipCode
        - city
        - countryCode
        - latitude
        - longitude
      properties:
        street:
          type: string
          example: Sonnenallee
        houseNumber:
          type: string
          example: 216A
        zipCode:
          type: string
          example: '12059'
        city:
          type: string
          example: Berlin
        countryCode:
          type: string
          description: Two-letter country codes as defined in ISO 3166-1 alpha-2
          minLength: 2
          maxLength: 2
          example: DE
        latitude:
          type: number
          description: Geographic latitude coordinate
          example: 50.9098881
        longitude:
          type: number
          description: Geographic longitude coordinate
          example: 9.5806327
    DeliveryNote:
      required:
        - type
        - createdAt
        - description
      properties:
        type:
          type: string
          enum:
            - HINT
        createdAt:
          type: string
          format: date-time
          example: '2022-01-15T14:33:25Z'
        description:
          type: string
          example: Vorderhaus 2. OG
    Unauthorized:
      required:
        - message
      properties:
        message:
          type: string
          example: Unauthorized
    Error:
      required:
        - id
      properties:
        id:
          type: string
          example: PARCEL-404
        reason:
          type: string
          example: The provided trackingId cannot be found.
        payload:
          type: string
          example: null
  securitySchemes:
    bearerToken:
      type: http
      scheme: bearer
      bearerFormat: JWT
