openapi: 3.1.0
info:
  version: 2.0.0
  title: Parcel Monitoring API
  description: Parcel Monitoring API - Managed by Tracking tribe
  license:
    name: Proprietary
    url: https://bettermile.com/legal-notice
  contact:
    name: Bettermile - Data Solutions Team
    email: datasolutions@bettermile.com
servers:
  - url: https://data-solutions-v2.bettermile.com
    description: Production Environment
tags:
  - name: Parcel Monitoring
    description: Endpoints related to parcel monitoring
paths:
  /sm/api/v2/parcel-tracking-subscribe:
    post:
      tags:
        - Parcel Monitoring
      description: >-
        This endpoint allows you to subscribe to tracking updates for a specific
        parcel. It provides detailed information about the parcel’s status,
        ensuring that it can be tracked throughout its journey.
      operationId: parcelTrackingSubscribe
      security:
        - ClientCredentialsFlow:
            - read
      parameters:
        - in: header
          name: tenant-id-on-behalf
          required: false
          description: Enhance Authorization with Tenant Context
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubscriptionRequestBody'
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ParcelTrackingUpdate'
        '400':
          description: Invalid parcel number
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Subscription400Error'
        '401':
          description: Unauthorized
        '404':
          description: Parcel not found
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: >-
                      #/components/schemas/SubscribedParcelNotAvailableForTracking404Error
                  - $ref: >-
                      #/components/schemas/SubscribedParcelNotOutForDelivery404Error
        '409':
          description: Subscription already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Subscription409Error'
        '500':
          description: Server error
  /sm/api/v2/latest-parcel-status/{jobInternalId}: {}
webhooks:
  availabilityEvent:
    post:
      summary: Availability Event
      description: Parcel is available for real time tracking
      operationId: availabilityEvent
      security:
        - sharedAccessSignature: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AvailabilityEvent'
      responses:
        '200':
          description: event received and processed
  trackingEvent:
    post:
      summary: Tracking Event
      description: Parcel tracking update
      operationId: trackingEvent
      security:
        - sharedAccessSignature: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ParcelTrackingUpdate'
      responses:
        '200':
          description: event received and processed
components:
  schemas:
    SubscriptionRequestBody:
      type: object
      properties:
        parcelNumber:
          type: string
        tenantId:
          type: string
      required:
        - parcelNumber
        - tenantId
    ParcelTrackingUpdate:
      type: object
      properties:
        parcelNumber:
          type: string
        tenantId:
          type: string
        status:
          $ref: '#/components/schemas/Status'
        statusType:
          $ref: '#/components/schemas/StatusType'
      required:
        - parcelNumber
        - tenantId
    Status:
      type: object
      properties:
        position:
          $ref: '#/components/schemas/Position'
        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'
        remainingStops:
          type: integer
          description: Number of driver's remaining stops before delivery of the parcel
        type:
          $ref: '#/components/schemas/ParcelStatusType'
      required:
        - position
        - etaTimestampMin
        - etaTimestampMax
        - remainingStops
        - type
    Position:
      type: object
      properties:
        lat:
          type: number
          description: Geographic latitude coordinate
          example: 50.9098881
        lng:
          type: number
          description: Geographic longitude coordinate
          example: 9.5806327
      required:
        - lat
        - lng
    ParcelStatusType:
      type: string
      enum:
        - OUT_FOR_DELIVERY
        - DELIVERED
        - NOT_DELIVERED
        - COLLECTED
        - NOT_COLLECTED
        - PICKED_UP
        - NOT_PICKED_UP
    StatusType:
      type: string
      enum:
        - NO_ETA_INFORMATION
        - OUTDATED_ETA_INFORMATION
        - SUCCESS
        - UNKNOWN
    ParcelStatusResponse:
      type: object
      properties:
        parcelNumber:
          type: string
        jobInternalId:
          type: string
          format: uuid
        depotId:
          type: string
        internalDepotId:
          type: string
        vehicleNumber:
          type: string
        tenantId:
          type: string
        type:
          $ref: '#/components/schemas/ParcelStatusType'
        polledAt:
          type: string
          format: date-time
        detailType:
          type: string
        deliveredAt:
          type: string
          format: date-time
        positionLat:
          type: number
          description: Geographic latitude coordinate
          example: 50.9098881
        positionLng:
          type: number
          description: Geographic longitude coordinate
          example: 9.5806327
        etaTimestamp:
          type: string
          format: date-time
        remainingStops:
          type: string
        tourInternalId:
          type: string
          format: uuid
        tourMode:
          $ref: '#/components/schemas/TourMode'
        upperBound:
          type: string
          format: date-time
        lowerBound:
          type: string
          format: date-time
      required:
        - jobInternalId
        - depotId
        - internalDepotId
        - vehicleNumber
        - type
        - polledAt
    TourMode:
      type: string
      enum:
        - REGULAR
        - SHADOWED
    AvailabilityEvent:
      type: object
      properties:
        parcelNumber:
          type: string
        tenantId:
          type: string
        createdAt:
          type: string
          format: date-time
    Subscription409Error:
      type: object
      required:
        - id
        - reason
      properties:
        id:
          type: string
          example: PARCEL-4
        reason:
          type: string
          example: Adding another subscription for this day is not supported.
        details:
          type: string
          description: Optional field for additional technical details.
          nullable: true
    Subscription400Error:
      type: object
      required:
        - id
        - reason
      properties:
        id:
          type: string
          example: PARCEL-1
        reason:
          type: string
          example: The provided jobInternalId is invalid.
        details:
          type: string
          description: Optional field for additional technical details.
          nullable: true
    SubscribedParcelNotAvailableForTracking404Error:
      type: object
      required:
        - id
        - reason
      properties:
        id:
          type: string
          example: PARCEL-5
        reason:
          type: string
          example: 'Subscription not allowed: no tracking availability for this parcel.'
        details:
          type: string
          description: Optional field for additional technical details.
          nullable: true
    SubscribedParcelNotOutForDelivery404Error:
      type: object
      required:
        - id
        - reason
      properties:
        id:
          type: string
          example: PARCEL-6
        reason:
          type: string
          example: 'Subscription not allowed: parcel is no longer out for delivery.'
        details:
          type: string
          description: Optional field for additional technical details.
          nullable: true
  securitySchemes:
    bearerToken:
      type: http
      scheme: bearer
      bearerFormat: JWT
    ClientCredentialsFlow:
      type: oauth2
      description: >-
        This API uses the OAuth 2.0 Client Credentials flow for
        machine-to-machine authentication.
      flows:
        clientCredentials:
          tokenUrl: https://auth.bettermile.com/api/v1/machines/token
          scopes:
            read: Grants read access to resources
    sharedAccessSignature:
      type: apiKey
      in: header
      name: Authorization
      description: >
        Use the SharedAccessSignature token in the `Authorization` header to
        authenticate the request.
security:
  - bearerToken: []
  - ClientCredentialsFlow:
      - read
      - write
