openapi: 3.1.0
info:
  x-api-id: d44523cd-443f-4d9a-bc74-43448ac90dd4
  x-audience:
    - external-partner
  title: Conundra Vehicle API
  version: 0.0.3
  description: >
    The Vehicle API provides endpoints for managing vehicles used in planning
    operations.
  contact:
    name: API support
    url: https://servicedesk.conundra.eu
servers:
  - url: https://api.conundra.eu/vehicle/v1
    description: Vehicle API Production
security:
  - clientCredentials: []
tags:
  - name: health
    x-displayName: Health check
    description: Miscellaneous endpoints for general API functionality
  - name: vehicle
    x-displayName: Vehicle [EXPERIMENTAL]
    description: |
      Endpoints for managing vehicles.
paths:
  /ping:
    get:
      tags:
        - health
      summary: Health check
      description: >
        The `ping`-endpoint can be used for validating a connection with the
        application. It will authenticate the caller and respond `pong` if
        everything is set-up correctly.
      operationId: ping
      responses:
        '200':
          description: >-
            Returned when the application is responsive and you are
            authenticated.
          content:
            text/plain:
              schema:
                type: string
              example: pong
        '401':
          $ref: '#/components/responses/401'
  /vehicles/{business_id}:
    put:
      tags:
        - vehicle
      summary: Upsert Vehicle
      operationId: upsertVehicle
      description: >
        Create or update a `Vehicle`. A unique ID (`business_id`) is required to
        identify the `Vehicle`. 

        Calling this endpoint with an existing `business_id` will update the
        `Vehicle`.


        Modifying (or retrieving) a `Vehicle` will provide the caller with a
        version number for this `Vehicle`. 

        This version can be passed to the _upsert_ endpoint as a _weak ETag_ in
        the `If-Match` header. 

        This allows the caller to ensure that the `Vehicle` has not been
        modified since the last time it was retrieved.
      parameters:
        - $ref: '#/components/parameters/business_id'
          description: >-
            Unique identifier for this `Vehicle`. Should only contain characters
            which do not need URL encoding.
        - $ref: '#/components/parameters/prefer'
        - $ref: '#/components/parameters/if-match'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Vehicle'
      responses:
        '201':
          description: Vehicle created or updated
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
            Preference-Applied:
              $ref: '#/components/headers/Preference-Applied'
            Location:
              $ref: '#/components/headers/Location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Vehicle'
        '204':
          description: Vehicle created or updated
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
            Preference-Applied:
              $ref: '#/components/headers/Preference-Applied'
            Location:
              $ref: '#/components/headers/Location'
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '404':
          $ref: '#/components/responses/404'
        '409':
          $ref: '#/components/responses/409'
        '412':
          $ref: '#/components/responses/412'
    get:
      tags:
        - vehicle
      operationId: getVehicle
      summary: Get Vehicle
      description: Get an existing Vehicle, defined by their `business_id`.
      parameters:
        - $ref: '#/components/parameters/business_id'
          description: |
            The unique ID (`business_id`) used to create this `Vehicle`.
      responses:
        '200':
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
          description: >
            The `Vehicle` was successfully retrieved. Details are in the
            response body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Vehicle'
        '401':
          $ref: '#/components/responses/401'
        '404':
          $ref: '#/components/responses/404'
    delete:
      tags:
        - vehicle
      operationId: deleteVehicle
      summary: Delete Vehicle
      description: |
        Delete a Vehicle, defined by their `business_id`.
      parameters:
        - $ref: '#/components/parameters/business_id'
        - $ref: '#/components/parameters/if-match'
      responses:
        '204':
          description: |
            Deletion has succeeded and body is empty.
        '400':
          $ref: '#/components/responses/400'
        '401':
          $ref: '#/components/responses/401'
        '404':
          $ref: '#/components/responses/404'
        '412':
          $ref: '#/components/responses/412'
components:
  parameters:
    business_id:
      name: business_id
      in: path
      description: A unique identifier of an entity instance.
      required: true
      schema:
        type: string
        pattern: ^[a-zA-Z0-9.~\-_]+$
        maxLength: 512
    prefer:
      name: Prefer
      in: header
      description: |
        The RFC7240 Prefer header indicates that a particular server behavior
        is preferred by the client but is not required for successful completion
        of the request (see [RFC 7240](https://tools.ietf.org/html/rfc7240)).

        The following behavior (see _Examples_) is supported by this API:
        * **return=<minimal|representation>** is used to suggest the server to
          return using 204 without resource (minimal) or using 200 or 201 with
          resource (representation) in the response body on success.
      schema:
        type: array
        items:
          type: string
      examples:
        minimal:
          value: return=minimal
          description: Indicate no response is needed, can be omitted
        representation:
          value: return=representation
          description: Request the result of the operation as response
    if-match:
      name: If-Match
      in: header
      description: Weak Etag used for optimistic locking.
      schema:
        type: string
        example: W/"24"
  schemas:
    Vehicle:
      type: object
      properties:
        business_id:
          type: string
          pattern: ^[a-zA-Z0-9.~\-_]+$
          maxLength: 512
          readOnly: true
          example: truck-42
          description: |
            Unique identifier for this `Vehicle`.
        description:
          type: string
          minLength: 1
          maxLength: 999
          example: Horn plays La Cucaracha - use sparingly in residential areas
          description: |
            Description of the vehicle.
        mandatory:
          type: boolean
          default: false
          example: true
          description: >
            Whether OptiFlow must use this vehicle in a planning or not. If
            false, OptiFlow will only use this vehicle when it makes sense to do
            so.
        driver:
          $ref: '#/components/schemas/Driver'
        start:
          $ref: '#/components/schemas/Start'
        end:
          $ref: '#/components/schemas/End'
        deployability:
          $ref: '#/components/schemas/Deployability'
        capabilities:
          type: array
          uniqueItems: true
          items:
            type: string
            minLength: 1
            maxLength: 999
          default: []
          example:
            - refrigerated
            - hazmat
          description: >
            Capabilities this vehicle provides.

            Used to make sure that orders requiring a specific capability, are
            only handled by vehicles with that capability.
        transport_resource:
          $ref: '#/components/schemas/TransportResource'
        labels:
          type: array
          uniqueItems: true
          items:
            type: string
          default: []
          example:
            - priority
            - express
          description: >
            Labels for categorizing vehicles. Can be used for facility filtering
            or defining constraints.
        cost:
          $ref: '#/components/schemas/Cost'
        route_settings:
          $ref: '#/components/schemas/RouteSettings'
        color:
          type: string
          example: '#FF5733'
          description: >
            The color used to display this vehicle's route in the planning
            overview.

            Accepts CSS color names (e.g., "red", "blue") or hexadecimal color
            codes (e.g., "#FF5733", "#123DEF").
        break_rule_group:
          type: string
          example: BELGIUM-LEGAL
          description: >
            Break Rule group for this vehicle. Allows bulk assignment of break
            rules to all vehicles in the group during planning.
      required:
        - start
        - end
        - deployability
    Driver:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 999
          example: John Doe
          description: |
            Name of the driver.
        efficiency_factor:
          type: number
          format: double
          default: 1
          minimum: 0.01
          maximum: 2
          example: 1.2
          description: >
            Factor applied to service times and location stop times. A value of
            1.0 (the default) does not alter the timings. Values < 1.0 increase
            the timings (lower efficiency), values > 1.0 decrease the timings
            (higher efficiency).
        custom_data:
          $ref: '#/components/schemas/CustomData'
    Start:
      type: object
      properties:
        location:
          $ref: '#/components/schemas/StartLocation'
        service_time:
          type: string
          default: PT0S
          example: PT20M
          description: |
            Service time at the start location, formatted as ISO-8601 duration.
        orders_to_deliver_available:
          type: boolean
          default: false
          example: true
          description: >
            Indicates whether the orders to deliver are already available at the
            start location.


            When true, the vehicle can begin deliveries immediately.


            When false, the vehicle must first drive to a depot to pick up
            orders before it can start delivering.


            Note: When the planning is a multi-tour VRP planning, only the
            orders to deliver on the first sub-tour are available at the start
            location.
      required:
        - location
    StartLocation:
      type: object
      properties:
        address:
          $ref: '#/components/schemas/Address'
      required:
        - address
    End:
      type: object
      properties:
        location:
          $ref: '#/components/schemas/EndLocation'
        service_time:
          type: string
          default: PT0S
          example: PT15M
          description: |
            Service time at the end location, formatted as ISO-8601 duration.
        allow_picked_up_orders:
          type: boolean
          default: false
          example: true
          description: >
            Indicates whether the vehicle can end its route with picked-up
            orders still on board.


            When true, the vehicle can drive directly to the end location with
            picked-up orders remaining on board.


            When false, the vehicle must visit a depot to unload all picked-up
            orders before driving to the end location.
      required:
        - location
    EndLocation:
      type: object
      description: |
        End location for the vehicle. Only one of the properties should be set.
      properties:
        address:
          $ref: '#/components/schemas/Address'
          description: |
            The address where the vehicle should end.
        at_last_activity:
          type: boolean
          description: |
            When true, the vehicle ends at the location of its last activity.
          example: false
        same_as_start:
          type: boolean
          description: |
            When true, the vehicle returns to its start location.
          example: false
    Address:
      type: object
      properties:
        business_id:
          type: string
          pattern: ^[a-zA-Z0-9.~\-_]+$
          maxLength: 512
          example: 50.9514048-3.8067878
          description: |
            Unique identifier for this address.
        name:
          type: string
          example: The office
          description: |
            Human-readable name for the location.
        address_line:
          type: string
          example: Voordries 41
          description: >
            The part of the address that includes the street name, house number,
            bus number etc. Required when omitting `geo_point`.
        city:
          type: string
          example: Oosterzele
          description: |
            City name. Required when omitting `geo_point`.
        zip_code:
          type: string
          example: '9860'
          description: |
            Postal code. Required when omitting `geo_point`.
        country_code:
          $ref: '#/components/schemas/CountryCode'
        geo_point:
          $ref: '#/components/schemas/GeoPoint'
      required:
        - business_id
    Deployability:
      type: object
      properties:
        window:
          $ref: '#/components/schemas/DeployabilityWindow'
        duration:
          $ref: '#/components/schemas/DeployabilityDuration'
      required:
        - window
    DeployabilityWindow:
      type: object
      properties:
        start:
          $ref: '#/components/schemas/DeployabilityWindowStart'
        end:
          $ref: '#/components/schemas/DeployabilityWindowEnd'
      required:
        - start
        - end
    DeployabilityWindowStart:
      type: object
      properties:
        earliest:
          type: string
          format: date-time
          example: '2025-01-01T08:00:00Z'
          description: |
            Earliest time the vehicle can start.
        latest:
          type: string
          format: date-time
          example: '2025-01-01T10:00:00Z'
          description: |
            Latest time the vehicle can start.
      required:
        - earliest
    DeployabilityWindowEnd:
      type: object
      properties:
        latest:
          type: string
          format: date-time
          example: '2025-01-01T18:00:00Z'
          description: |
            Latest time the vehicle must end.
      required:
        - latest
    DeployabilityDuration:
      type: object
      description: |
        When specified, one of both durations must be > PT0S
      properties:
        at_most_without_overtime:
          type: string
          example: PT8H
          description: >
            Maximum deployment duration, without overtime, formatted as ISO-8601
            duration.
        allowed_overtime:
          type: string
          example: PT2H
          description: >
            Additional overtime allowed, formatted as ISO-8601 duration. When
            specified and > PT0S, the `overtime_per_hour` cost must also be
            provided.
      required:
        - at_most_without_overtime
        - allowed_overtime
    TransportResource:
      type: object
      properties:
        license_plate:
          type: string
          minLength: 1
          maxLength: 99
          example: 1-ABC-234
          description: |
            License plate of the transport resource.
        capacities:
          type: array
          uniqueItems: true
          items:
            $ref: '#/components/schemas/Capacity'
          default: []
          example:
            - type: weight
              value: 3500
            - type: volume
              value: 25.5
          description: |
            Capacity specifications for the transport resource.
        custom_data:
          $ref: '#/components/schemas/CustomData'
    Capacity:
      type: object
      properties:
        type:
          $ref: '#/components/schemas/CapacityType'
        value:
          type: number
          format: double
          minimum: 0
          example: 100
          description: |
            Capacity value.
      required:
        - type
        - value
    CapacityType:
      type: string
      enum:
        - volume
        - weight
        - loadingMeters
        - capacity1
        - capacity2
        - capacity3
        - capacity4
        - capacity5
      description: Type of capacity measurement
    Cost:
      type: object
      properties:
        fixed:
          type: number
          format: double
          minimum: 0
          default: 0
          example: 50
          description: Fixed cost for using this vehicle.
        per_hour:
          type: number
          format: double
          minimum: 0
          default: 0
          example: 25
          description: >-
            Hourly cost applied when this vehicle is in use, including drive
            time, wait time, and service time.
        per_kilometer:
          type: number
          format: double
          minimum: 0
          default: 0
          example: 0.5
          description: |
            Cost per kilometer.
        per_stop:
          type: number
          format: double
          minimum: 0
          default: 0
          example: 12
          description: >
            Cost per stop this vehicle makes. Note that stops at depots or
            breaks are not considered as separate stops.
        overtime_per_hour:
          type: number
          format: double
          exclusiveMinimum: 0
          default: 0
          example: 30
          description: >
            Cost per hour overtime. When specified, the `deployability`
            durations must also be provided and `allowed_overtime` must be >
            PT0S.
    RouteSettings:
      type: object
      properties:
        routing_group:
          type: string
          example: medium-truck
          description: >
            Routing group for this vehicle. Allows bulk assignment of routing
            profiles to all vehicles in the group during planning.
        max_distance_in_kilometers:
          type: number
          format: double
          exclusiveMinimum: 0
          example: 450
          description: >
            Maximum total distance for this vehicle's route, calculated as the
            sum of all subtour distances.
        max_stops:
          type: integer
          minimum: 1
          example: 10
          description: |
            Maximum number of stops allowed per route for this vehicle.
        driving_speed_factor:
          type: number
          format: double
          exclusiveMinimum: 0
          maximum: 2
          default: 1
          example: 1.5
          description: |
            Multiplier applied to this vehicle's default speed.
        preferred_route_duration:
          $ref: '#/components/schemas/PreferredRouteDuration'
    PreferredRouteDuration:
      type: object
      properties:
        duration:
          type: string
          example: PT8H
          description: >
            The preferred duration of the route assigned to the vehicle.

            If omitted, the preferred route duration of this vehicle is the
            average route duration.
        importance:
          type: number
          format: double
          minimum: 0
          maximum: 1
          example: 0.5
          description: >
            A scale between 0 and 1 resembling a tradeoff between minimizing the
            total hour cost and steering towards the preferred duration of the
            vehicle.

            Higher values indicate a stronger preference for a route duration
            close to the preferred one.

            As the cost per hour increases, the influence of this tradeoff
            becomes more significant.
      required:
        - importance
    GeoPoint:
      properties:
        latitude:
          type: number
          format: double
          example: 50.9514048
          minimum: -90
          maximum: 90
        longitude:
          type: number
          format: double
          example: 3.8067878
          minimum: -180
          maximum: 180
        resolved:
          readOnly: true
          type: object
          description: Geocoding resolution information.
          properties:
            geocoded:
              type: boolean
              description: >
                Indicates if the provided address was geocoded or not.

                Geocoding only happens when the provided address does not yet
                have a geo-point.
              example: true
            address:
              type: string
              description: >-
                The address that matches the resolved geo-point. Only present
                when `geocoded` is `true`.
              example: Voordries 41, 9860 Oosterzele
            precision:
              type: integer
              description: >
                Geocoding confidence score from 0-10.

                10 indicates 100% confidence that the coordinates match the
                provided address. 0 indicates no confidence in the match.

                Only present when `geocoded` is `true`.
              minimum: 0
              maximum: 10
              example: 9
      required:
        - latitude
        - longitude
    CountryCode:
      description: >-
        A ISO3166-1 alpha-2 two letter country code. Required when omitting
        `geo_point`.
      type: string
      pattern: ^[A-Z]{2}$
      example: BE
    CustomData:
      title: CustomData
      description: >
        Custom key-value pairs for client usage.

        These values have no meaning in the context of OptiFlow, but can be used
        to enrich the data with relevant information for the end users.


        Keys should not be blank nor contain periods.

        Keys with null values are not retained (same as omitting the key). Use
        an empty string ("") to retain keys without a concrete value.
      additionalProperties:
        type: string
      default: {}
      example:
        something: customer specific
    Problem:
      type: object
      properties:
        title:
          type: string
          example: Validation exception
          description: A short, summary of the problem.
        detail:
          type: string
          description: A detailed explanation specific to this problem.
          example: The field contains invalid business formatting.
    ProblemWithViolations:
      allOf:
        - $ref: '#/components/schemas/Problem'
        - type: object
          properties:
            violations:
              type: array
              items:
                $ref: '#/components/schemas/Violation'
    Violation:
      type: object
      properties:
        field:
          type: string
          example: path.to.field
        message:
          type: string
          example: Field is required
  responses:
    '400':
      description: Bad request
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemWithViolations'
    '401':
      description: Authentication required
    '404':
      description: Not found
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    '409':
      description: Conflict
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    '412':
      description: >
        This status is returned when an trying to modify an entity with an
        outdated version in the If-Match header.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  headers:
    ETag:
      description: >-
        A weak Etag (ex `W/"<etag_value>"`) which contains the current version
        of the entity
      schema:
        type: string
        example: W/"25"
    Preference-Applied:
      description: Indicates whether a preference has been applied.
      schema:
        type: string
        example: return=representation
    Location:
      description: A URI to access the result of the operation.
      schema:
        type: string
        format: URI
  securitySchemes:
    clientCredentials:
      type: oauth2
      description: >
        This API uses OAuth2 with the Client Credentials flow for M2M
        communication. Client ID ans secret should be provided as a HTTP Basic
        Auth header.
      flows:
        clientCredentials:
          tokenUrl: >-
            https://auth.conundra.eu/auth/realms/prod/protocol/openid-connect/token
          scopes: {}
