openapi: 3.0.1
info:
  title: Job Management API
  # The version must be increased whenever the API is changed!
  # This version is also used to version the maven packages for model, clients and server.
  # In oder to let the job manager service implement the latest version of the server API,
  # you have to adjust the version.api property in the pom file of the job manager.
  version: '1.16'
  description: >
    API to create and manipulate jobs. A Job describes a number of milestones which should be executed
    together and by a single transport resource. 
    There are multiple other APIs which might be of interest: Operations API to visualize milestones in the 
    frontend and Job Event API to receive updates on the status of existing jobs.

servers:
  - url: http://localhost:8182
    description: Generated server url

tags:
  - name: Job Management
    description: This API provides REST API endpoints for creating and updating jobs.

paths:
  /api/v1/jobs:
    get:
      tags:
        - Job Management
      summary: Returns all exiting jobs
      description: This operation returns all existing jobs with their milestones
      operationId: getAllJobs
      parameters:
        - in: query
          name: finishedLessThanSecondsAgo
          description: >
            Defines that a job is allowed to be finished/discarded only before the given amount of seconds to be 
            part of the result. Not finished jobs will always be included.
          required: false
          schema:
            type: integer
      responses:
        200:
          description: The request was successful and the list of jobs is returned
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ReadJob'
      security:
        - oauthToken:
            - FPOS_ADMIN
    post:
      tags:
        - Job Management
      summary: Creates a new job
      description: This operation creates a new job.
      operationId: createJob
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateJob'
        required: true
      responses:
        "201":
          description: The request was successful and a job was created.
          headers:
            Location:
              description: An absolute URI representation of the location of the created job.
              style: simple
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReadJob'
        "400":
          description: There was a problem validating the input.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "500":
          description: There was an internal server error.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'

      security:
        - oauthToken:
            - FPOS_ADMIN
  /api/v1/jobs/{jobId}:
    get:
      tags:
        - Job Management
      summary: Returns a specific job
      description: This operation returns the job for the given ID.
      operationId: getJob
      parameters:
        - name: jobId
          in: path
          description: The id of the job
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: The request was successful and the job is returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReadJob'
        "404":
          description: The requested resource was not found.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "500":
          description: There was an internal server error.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      security:
        - oauthToken:
            - FPOS_ADMIN
    patch:
      tags:
        - Job Management
      summary: Assigns a resource to a job
      description: >
        This command tries to (re)assign a transport resource to the specified job together with
        relevant time information. If the (re)assignment is successful, JobAssignedEvent is published. 
        If the (re)assignment is not successful, JobAssignmentRejectedEvent is published.
        Once there's a milestone already started performing, reassignment is not possible anymore. Otherwise,
        it's possible to reassign the targeted job to another resource.
      operationId: assignTransportResource
      parameters:
        - name: jobId
          in: path
          description: Id of the job which should be updated.
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssignJob'
        required: true
      responses:
        204:
          description: The request was successful and the transport resource was successfully assigned to the job.
        400:
          description: There was a problem validating the input.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        500:
          description: There was an internal server error.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      security:
        - oauthToken:
            - FPOS_ADMIN
  /api/v1/jobs/{jobId}/discard-request:
    put:
      tags:
        - Job Management
      summary: Requests to discard job
      description: >
        This command requests to discard the specified job. The update might not happen immediately
        depending on the current milestone and job status. Discard is always possible as long as the job is not in 
        a final state.
      operationId: discardJob
      parameters:
        - name: jobId
          in: path
          description: Id of the job which should be discarded.
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DiscardJob'
        required: true
      responses:
        202:
          description: The request was successful and the request to discard the job was accepted.
        400:
          description: There was a problem validating the input.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        500:
          description: There was an internal server error.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      security:
        - oauthToken:
            - FPOS_ADMIN
components:
  schemas:
    Problem:
      type: object
      properties:
        type:
          type: string
          format: uri-reference
          enum: [ "https://synaos.com/fpos/problems/invalid-user-input" ]
          description: "A URI reference that identifies the problem type. In contrast\
            \ to RFC7807, it is not meant to be dereferenced."
        title:
          type: string
          description: "A short, human-readable summary of the problem type. It is\
            \ written in english and not localized."
        status:
          maximum: 600
          minimum: 100
          type: integer
          description: The HTTP status code generated by the origin server for this
            occurrence of the problem.
          format: int32
        detail:
          type: string
          description: A human-readable explanation specific to this occurrence of
            the problem. It is written in english and not localized.
        instance:
          type: string
          description: "A representation of the problem instance. Might be left blank\
            \ for some problems. In contrast to RFC7807, it is not a URI and not meant\
            \ to be dereferenced."
        extensions:
          type: object
          additionalProperties:
            type: object
            description: Extensions used for internal purposes. Do not rely on these
              being set and/or stable.
          description: Extensions used for internal purposes. Do not rely on these
            being set and/or stable.
          deprecated: true
        translatableExtensions:
          type: object
          additionalProperties:
            type: object
            description: Extensions used for internal purposes. Do not rely on these
              being set and/or stable.
          description: Extensions used for internal purposes. Do not rely on these
            being set and/or stable.
          deprecated: true
      description: Problem details as proposed in https://tools.ietf.org/html/rfc7807
    Address:
      required:
        - id
      type: object
      properties:
        system:
          maxLength: 255
          minLength: 1
          type: string
          description: >
            Reference to a system where the id is uniquely resolvable.
            For a node, the system is the navigation graph it belongs to. If the
            id is globally unique (e.g. for handling stations, handling station
            groups or charging stations), system must not be set.
        id:
          maxLength: 255
          minLength: 1
          type: string
          description: >
            Id of node, handling station, handling station group or charging
            station
      description: >
        An address that can be navigated to in a customer specific address system
    MilestoneActionParameters:
      type: object
      properties:
        chargeLevelCutoffPercent:
          maximum: 100
          minimum: 0
          type: integer
          description: Target battery percentage of charge action
          format: int32
        load:
          $ref: '#/components/schemas/Load'
      description: Additional parameters that are needed for the action
    Load:
      description: >
        A load is a set of items and/or carriers.
        Carriers can contain other loads.
      type: object
      properties:
        carriers:
          description: >
            A carrier is a container for more carriers or items, such as a box, a palette, a corlette,
            or a trailer (a moving carrier that is fitted to driving vehicle and may be picked/dropped/swapped with another trailer.
            A trailer may contain other loads like carrier and/or items).
          type: array
          items:
            $ref: '#/components/schemas/Carrier'
        items:
          type: array
          items:
            $ref: '#/components/schemas/Item'
    Carrier:
      type: object
      properties:
        carrierId:
          type: string
          description: Unique identifier
        carrierType:
          type: string
          description: The type of this carrier
        content:
          $ref: '#/components/schemas/Load'
    Item:
      type: object
      required:
        - itemId
        - quantity
      properties:
        itemId:
          type: string
          description: >
            unique identifier of an item to be moved as part of an
            intralogistics process, e.g., an order item, or a part for manufacturing.
        quantity:
          type: integer
          format: int32
          description: Item quantity
    CreateJob:
      required:
        - milestones
      type: object
      properties:
        id:
          type: string
          description: Id of the job. Auto generated if not set.
          format: uuid
        assignedResourceId:
          maxLength: 255
          minLength: 1
          type: string
          description: >
            Id of the transport resource which should execute the job. Will be assigned via the assign job endpoint
            when not provided here. Also consider defining planned times in the milestones to achieve proper execution 
            of the job.
        executionConflictStrategy:
          type: object
          description: >
            An execution conflict occurs when a job should be executed (according to its start time), 
            but another job for the same transport resource is already (or still) in execution. 
            This strategy defines how jobs behave if they can't be executed and when already running jobs are cancelled
            due to such an execution conflict.
          properties:
            priority:
              type: integer
              minimum: 0
              maximum: 10
              default: 5
              description: >
                In case of an execution conflict the priority defines which job will be executed (higher priority) and which
                will be handled by its execution conflict resolution (lower priority). If the lower priority job is already in execution
                the execution will be cancelled before the conflict resolution is applied. In case of the same 
                priority the already running job will not be cancelled, but the inactive job will be handled by its 
                conflict resolution.
                10 is the highest and 0 the lowest possible priority.
            resolution:
              type: string
              default: ENQUEUE
              x-extensible-enum:
                - DISCARD
                - ENQUEUE
              description: >
                The resolution defines what should happen with a job which can't be executed as planned due to a 
                execution conflict. See the priority descriptions for further insights when it will be applied.
                When the resolution is *ENQUEUE* the job will be kept and executed as soon as possible (therefore
                after all jobs for that resource with a higher priority or the same priority and an earlier start time). 
                In case of *DISCARD* the job will fail and never be executed anymore.
            handleExecutionStoppedAsConflict:
              type: boolean
              default: true
              description: >
                If set to *true* then the defined conflict strategy will be applied to the job when the execution of one 
                milestone was stopped. If set to *false* the job will remain in stopped state and wait for an external resolution.
        milestones:
          description: >
            A milestone is one unit of work that needs to be finished to fulfill
            the whole job. It represents a business need from the perspective of the
            creator of the job, not all possible interactions that might be necessary
            to fulfill it. E.g. even though a PICK milestone might include moving to
            the target location first, it does not require a MOVE milestone to be executed
            previously. The lifecycle of a milestone consists of different states: PENDING
            > APPROACHING > ARRIVED > PERFORMING > FINISHED. Being qualified to progress
            to another state might be blocked by the state of another milestone required
            to be at least assumed first. This can be used to represent dependencies
            across different jobs like when bringing a new container is only allowed
            if the container station is emptied by another job first. However, a qualification
            might also contain other conditions. A milestone can only be qualified to
            allow approaching if the location could already be resolved to an approachable
            address. A milestone can have two addresses. The mandatory address defines (roughly)
            where the action has to take place. The optional recommended address gives a hint
            where to execute the action exactly. The resource have to fulfill the address
            requirements but is allowed to deviate regarding the recommended address.
          minItems: 1
          type: array
          items:
            $ref: '#/components/schemas/CreateMilestone'
        correlations:
          description: >
            A set of correlations that are not considered for any business logic in Job-Manager.
            They can be attached to the job for different use cases.
            i.e) filter for job events or relate jobs to the corresponding order.
          type: array
          items:
            $ref: '#/components/schemas/Correlation'
      description: >
        An intralogistics job, which consists of steps, called job milestones. All milestones will
        be executed by the same transport resource.
    CreateMilestone:
      required:
        - action
        - address
      type: object
      properties:
        action:
          type: string
          description: >
            The action required to be done by the transport resource. Expected values are:
            MOVE, PICK, DROP, CHARGE, PROVIDE, COUNT
          #           Added into the description as the html generator didn't show the values
          x-extensible-enum:
            - MOVE
            - PICK
            - DROP
            - CHARGE
            - PROVIDE
            - COUNT
        address:
          $ref: '#/components/schemas/Address'
        recommendedAddress:
          $ref: '#/components/schemas/Address'
        actionParameters:
          $ref: '#/components/schemas/MilestoneActionParameters'
        plannedTimes:
          $ref: '#/components/schemas/PlannedTimes'
        demandTimes:
          $ref: '#/components/schemas/DemandTimes'
        correlations:
          description: >
            A set of correlations that are not considered for any business logic in Job-Manager.
            They can be attached to the milestone of the job for different use cases.
            i.e) filter for job events or relate jobs to the corresponding order.
          type: array
          items:
            $ref: '#/components/schemas/Correlation'
    ReadJob:
      required:
        - id
        - executionConflictStrategy
        - status
        - milestones
        - correlations
      type: object
      properties:
        id:
          type: string
          description: Id of the job
          format: uuid
        assignedResourceId:
          maxLength: 255
          minLength: 1
          type: string
          description: Id of transport resource that is assigned to the job
        executionConflictStrategy:
          required:
            - priority
            - resolution
          type: object
          description: >
            An execution conflict occurs when a job should be executed (according to its start time), 
            but another job for the same transport resource is already (or still) in execution. 
            This strategy defines how jobs behave if they can't be executed and when already running jobs are cancelled
            due to such an execution conflict.
          properties:
            priority:
              type: integer
              minimum: 0
              maximum: 10
              description: >
                In case of an execution conflict the priority defines which job will be executed (higher priority) and which
                will be handled by its execution conflict resolution (lower priority). If the lower priority job is already in execution
                the execution will be cancelled before the conflict resolution is applied. In case of the same 
                priority the already running job will not be cancelled, but the inactive job will be handled by its 
                conflict resolution.
                10 is the highest and 0 the lowest possible priority.
            resolution:
              type: string
              x-extensible-enum:
                - DISCARD
                - ENQUEUE
              description: >
                The resolution defines what should happen with a job which can't be executed as planned due to a 
                execution conflict. See the priority descriptions for further insights when it will be applied.
                When the resolution is *ENQUEUE* the job will be kept and executed as soon as possible (therefore
                after all jobs for that resource with a higher priority or the same priority and an earlier start time). 
                In case of *DISCARD* the job will fail and never be executed anymore.
            handleExecutionStoppedAsConflict:
              type: boolean
              description: >
                If set to *true* then the defined conflict strategy will be applied to the job when the execution of one 
                milestone was stopped. If set to *false* the job will remain in stopped state and wait for an external resolution.
        status:
          type: string
          description: The current status of the job. The value might change in between multiple calls to this API
          enum:
            - CREATED
            - RELEASED
            - FINISHED_SUCCESS
            - FINISHED_FAILURE
          deprecated: true
        milestones:
          description: >
            A milestone is one unit of work that needs to be finished to fulfill
            the whole job. It represents a business need from the perspective of the
            creator of the job, not all possible interactions that might be necessary
            to fulfill it. E.g. even though a PICK milestone might include moving to
            the target location first, it does not require a MOVE milestone to be executed
            previously. The lifecycle of a milestone consists of different states: PENDING
            > APPROACHING > ARRIVED > PERFORMING > FINISHED. Being qualified to progress
            to another state might be blocked by the state of another milestone required
            to be at least assumed first. This can be used to represent dependencies
            across different jobs like when bringing a new container is only allowed
            if the container station is emptied by another job first. However, a qualification
            might also contain other conditions. A milestone can only be qualified to
            allow approaching if the location could already be resolved to an approachable
            address. A milestone can have two addresses. The mandatory address defines roughly
            where the action have to take place. The optional recommended address gives a hint
            where to execute the action exactly. The resource have to fulfill the address
            requirements but is allowed to deviate regarding the recommended address.
          minItems: 1
          type: array
          items:
            $ref: '#/components/schemas/ReadMilestone'
        correlations:
          description: The set of correlations that were provided when the job was created
          type: array
          items:
            $ref: '#/components/schemas/Correlation'
      description: "An intralogistics task, which consists of steps, called job milestones"
    ReadMilestone:
      required:
        - id
        - action
        - address
        - statusChanges
        - correlations
      type: object
      properties:
        id:
          type: string
          description: Id of the milestone
          format: uuid
        action:
          type: string
          description: >
            The action required to be done by the resource. Expected values are:
            MOVE, PICK, DROP, CHARGE, PROVIDE, COUNT
          #           Added into the description as the html generator didn't show the values
          x-extensible-enum:
            - MOVE
            - PICK
            - DROP
            - CHARGE
            - PROVIDE
            - COUNT
        address:
          $ref: '#/components/schemas/Address'
        recommendedAddress:
          $ref: '#/components/schemas/Address'
        actionParameters:
          $ref: '#/components/schemas/MilestoneActionParameters'
        status:
          $ref: '#/components/schemas/MilestoneStatus'
        statusChanges:
          type: array
          items:
            $ref: '#/components/schemas/MilestoneStatusChange'
        eventHistory:
          type: array
          items:
            oneOf:
              - $ref: '#/components/schemas/MilestoneEvent'
              - $ref: '#/components/schemas/MilestoneExecutionStoppedEventLog'
              - $ref: '#/components/schemas/MilestoneDiscardedEventLog'
            discriminator:
              propertyName: name
              mapping:
                MILESTONE_APPROACHING_STARTED: '#/components/schemas/MilestoneEvent'
                MILESTONE_APPROACHED: '#/components/schemas/MilestoneEvent'
                MILESTONE_PERFORMING_STARTED: '#/components/schemas/MilestoneEvent'
                MILESTONE_FINISHED: '#/components/schemas/MilestoneEvent'
                MILESTONE_FAILED: '#/components/schemas/MilestoneEvent'
                MILESTONE_DISCARDED: '#/components/schemas/MilestoneDiscardedEventLog'
                MILESTONE_STOPPED: '#/components/schemas/MilestoneExecutionStoppedEventLog'
                MILESTONE_EXECUTION_STOPPED: '#/components/schemas/MilestoneExecutionStoppedEventLog'
        startTime:
          description: >
            The current best guess about the start time of the milestone. Can be a planned, estimated or actual time,
            depending on the milestone state.
          type: string
          format: date-time
        finishTime:
          description: >
            The current best guess about the finish time of the milestone. Can be a planned, estimated or actual time,
            depending on the milestone state.
          type: string
          format: date-time
        demandTimes:
          $ref: '#/components/schemas/DemandTimes'
        delay:
          description: >
            Delay of the milestone in seconds. It can be expected delay if the milestone is not finished yet, 
            or final delay when milestone is finished. When current finish time is not available yet, this will be null. 
            The delay is calculated by comparing the finishTime and demandTimes.
            When demandTimes are not provided, this value will be null. If finishTime meets demandTimes, it will be 0.
            Otherwise, the delay is less than zero when the finishTime is before earliest_demand_time. 
            Or the delay is greater than zero when the finishTime is after latest_demand_time.
          type: integer
        executionStoppedReason:
          description: The reason why a milestone execution was stopped. It will be only available when a milestone is in a stopped state.
          type: string
        correlations:
          type: array
          items:
            $ref: '#/components/schemas/Correlation'
    Correlation:
      required:
        - id
        - kind
      type: object
      properties:
        id:
          type: string
        kind:
          type: string
          description: 'Kind of reference, e.g., ''PLANSTEP'' or ''MANUAL'''
    MilestoneStatus:
      type: string
      description: >
        The current status of the milestone. The value might change in between multiple calls to this API.
        This property is deprecated and will not be provided in the response anymore.
      enum:
        - MILESTONE_NOT_STARTED
        - MILESTONE_APPROACHING_STARTED
        - MILESTONE_APPROACHED
        - MILESTONE_PERFORMING_STARTED
        - MILESTONE_FINISHED
        - MILESTONE_FAILED
      deprecated: true
    MilestoneStatusChange:
      type: object
      description: >
        The status change contains a status and the time when the status has been set. The list is ordered by change time
        and the natural ordering of the statuses. A status may be contained multiple times or not at all.
        This property is deprecated because the required property 'status' is deprecated. 
        Instead, "MilestoneEvent" is introduced to provide the history of milestone events.
      required:
        - status
      properties:
        status:
          $ref: '#/components/schemas/MilestoneStatus'
        changeTime:
          type: string
          format: date-time
          description: >
            UTC timestamp when the status was set.
      deprecated: true
    MilestoneEvent:
      type: object
      description: >
        The milestone event includes the type and published time of the milestone event.
        Milestone event is published when milestone status changes.
      properties:
        name:
          type: string
          x-extensible-enum:
            - MILESTONE_APPROACHING_STARTED
            - MILESTONE_APPROACHED
            - MILESTONE_PERFORMING_STARTED
            - MILESTONE_FINISHED
          description: >
            The name of the published milestone event. Expected values are:
            MILESTONE_APPROACHING_STARTED,  MILESTONE_APPROACHED, MILESTONE_PERFORMING_STARTED, MILESTONE_FINISHED
        time:
          type: string
          format: date-time
          description: >
            UTC timestamp when the event was published.
      required:
        - name
    MilestoneExecutionStoppedEventLog:
      type: object
      description: >
        The log includes the type, the published time of the milestone event and the reason why the milestone was stopped.
        Milestone event is published when milestone status changes.
      properties:
        name:
          type: string
          description: Must be "MILESTONE_EXECUTION_STOPPED"
          default: MILESTONE_EXECUTION_STOPPED
        time:
          type: string
          format: date-time
          description: >
            UTC timestamp when the event was published.
        reason:
          type: string
          description: The reason why the milestone has been stopped
      required:
        - name
    MilestoneDiscardedEventLog:
      type: object
      description: >
        The log includes the type, the published time of the milestone event and the reason why the milestone was discarded.
        Milestone event is published when milestone status changes.
      properties:
        name:
          type: string
          description: Must be "MILESTONE_DISCARDED"
          default: MILESTONE_DISCARDED
        time:
          type: string
          format: date-time
          description: >
            UTC timestamp when the event was published.
        reason:
          type: string
          description: The reason why the milestone has been discarded
      required:
        - name
    AssignJob:
      type: object
      properties:
        id:
          description: >
            Id of the job which should be updated.
          type: string
          format: uuid
        resourceId:
          maxLength: 255
          minLength: 1
          type: string
          description: >
            The id of the transport resource which should execute the job.
        plannedMilestoneTimes:
          type: array
          description: >
            Relevant times for the milestone execution.
          items:
            $ref: '#/components/schemas/PlannedMilestoneTimes'
      required:
        - resourceId
        - id
    PlannedMilestoneTimes:
      type: object
      properties:
        id:
          description: >
            The id of a milestone which is part of the job.
          type: string
          format: uuid
        startTime:
          $ref: '#/components/schemas/PlannedStartTime'
        finishTime:
          $ref: '#/components/schemas/PlannedFinishTime'
      required:
        - id
    PlannedTimes:
      type: object
      description: >
        Relevant times for the milestone execution of an assigned job. Values set here are only considered when also a 
        transport resource is assigned, as they would be overridden by the assign job endpoint anyway.
      properties:
        startTime:
          $ref: '#/components/schemas/PlannedStartTime'
        finishTime:
          $ref: '#/components/schemas/PlannedFinishTime'
    PlannedStartTime:
      description: >
        At this (UTC) time point the execution of the milestone is planned to start. It should be selected in a way
        that the transport resource is just able to meet the planned finish time. The planned start time influences 
        the execution order of milestones with the same transport resource.
        If this value is not provided it will default to the time the job manager receives this command.
      type: string
      format: date-time
    PlannedFinishTime:
      description: >
        At this (UTC) time point the execution of the milestone is planned to be finished. The time will be used for
        delay calculation and overridden with estimates as soon as the milestone execution starts.
      type: string
      format: date-time
    DemandTimes:
      type: object
      description: >
        Demand times mean the requested time frame of the milestone to be completed. It can be set at the time 
        of job creation. Either one of the demand times or both can be set.
      properties:
        earliestTime:
          $ref: '#/components/schemas/EarliestDemandTime'
        latestTime:
          $ref: '#/components/schemas/LatestDemandTime'
    EarliestDemandTime:
      description: >
        The milestone is required to be completed after this (UTC) time point. The earliest demand time influences 
        the execution order of milestones with the same transport resource if this value is provided.
      type: string
      format: date-time
    LatestDemandTime:
      description: >
        The milestone is required to be completed before this (UTC) time point. The latest demand time influences 
        the execution order of milestones with the same transport resource if this value is provided.
      type: string
      format: date-time
    DiscardJob:
      type: object
      properties:
        id:
          description: >
            Id of the job which should be discarded.
          type: string
          format: uuid
        reason:
          description: >
            The reason why the job should be discarded. Should be informative for the user of the application.
          type: string
      required:
        - id
        - reason
  securitySchemes:
    oauthToken:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: "https://example.com/token"
          scopes:
            FPOS_ADMIN: admin role
