openapi: 3.0.3
info:
  title: vyro - inventory-api
  contact:
    email: engineering@vyro.com.au
  version: 1.0.0
paths:
  /events/assign-stocked-vehicle-stock-id:
    post:
      summary: Handle assign-stocked-vehicle-stock-id Hasura event
      operationId: assignStockedVehicleStockId
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HasuraEvent"
      responses:
        201:
          description: Accepted
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        400:
          $ref: "#/components/responses/BadRequest"
        401:
          $ref: "#/components/responses/NotAuthenticated"
        403:
          $ref: "#/components/responses/NotAuthorised"
  /events/store-stocked-vehicle-pricing-history:
    post:
      summary: Handle store-stocked-vehicle-pricing-history Hasura event
      operationId: storeStockedVehiclePricingHistory
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HasuraEvent"
      responses:
        201:
          description: Accepted
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        400:
          $ref: "#/components/responses/BadRequest"
        401:
          $ref: "#/components/responses/NotAuthenticated"
        403:
          $ref: "#/components/responses/NotAuthorised"
  /batch:
    post:
      description: Consumes a batch of inventory (aka Vehicles, Listings)
      operationId: createBatch
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - items
              properties:
                source_name:
                  type: string
                  nullable: true
                  description: The name of the source that is creating the batch. This is used to identify the source of the data. If not provided, a source name will be created for you.
                  example:
                    - "app:[acc code]"
                    - "user:[user id]"
                items:
                  type: array
                  items:
                    $ref: "#/components/schemas/InventoryBulkRequestItem"
                  maxItems: 100
                  minItems: 1
                  description: An array of inventory items to create. Maximum 100 items per request, or at most 2MB of data.
                moderate_images:
                  type: boolean
                  description: Whether to moderate image content against the Vyro acceptable use policy.
                  default: true
                moderate_content:
                  type: boolean
                  description: Whether to moderate text content against the Vyro acceptable use policy.
                  default: true

      responses:
        201:
          description: Accepted
          content:
            application/json:
              schema:
                type: object
                required:
                  - job_id
                properties:
                  job_id:
                    type: string
                    format: uuid
        400:
          $ref: "#/components/responses/BadRequest"
        401:
          $ref: "#/components/responses/NotAuthenticated"
        403:
          $ref: "#/components/responses/NotAuthorised"
  /jobs/{job_id}:
    get:
      description: Gets the status of a job
      operationId: getJob
      parameters:
        - name: job_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        200:
          description: Job found.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                  status:
                    $ref: "#/components/schemas/JobStatus"
                  errors:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  image_moderation:
                    type: array
                    items:
                      type: object
                      properties:
                        job_item_id:
                          type: string
                          format: uuid
                        index:
                          type: number
                        accepted:
                          type: array
                          items:
                            $ref: "#/components/schemas/ModeratedImage"
                        rejected:
                          type: array
                          items:
                            $ref: "#/components/schemas/ModeratedImage"

        400:
          $ref: "#/components/responses/BadRequest"
        401:
          $ref: "#/components/responses/NotAuthenticated"
        403:
          $ref: "#/components/responses/NotAuthorised"
        404:
          $ref: "#/components/responses/NotFound"

components:
  responses:
    BadRequest:
      description: The request body is invalid
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    NotFound:
      description: The record could not be found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    NotAuthenticated:
      description: The user is not authenticated
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    NotAuthorised:
      description: The user is not authorised to perform this action
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
  schemas:
    HasuraEventStockedVehicle:
      type: object
      additionalProperties: true
      nullable: true
      properties:
        id:
          type: string
        stock_id:
          type: string
          nullable: true
        created_at:
          type: string
        updated_at:
          type: string
          nullable: true
        pricing_config:
          type: object
          additionalProperties: true
          nullable: true
        min_price:
          type: number
          nullable: true
      required:
        - id
        - created_at

    HasuraEvent:
      type: object
      properties:
        id:
          type: string
        event:
          type: object
          properties:
            op:
              type: string
              enum: [INSERT, UPDATE, DELETE, MANUAL]
            data:
              type: object
              properties:
                old:
                  anyOf:
                    - $ref: "#/components/schemas/HasuraEventStockedVehicle"
                new:
                  anyOf:
                    - $ref: "#/components/schemas/HasuraEventStockedVehicle"
              required:
                - old
                - new
          required:
            - op
            - data
        session_variables:
          type: object
          properties:
            x-hasura-user-id:
              type: string
            x-hasura-role:
              type: string
          required:
            - x-hasura-user-id
            - x-hasura-role
        trigger:
          type: object
          properties:
            name:
              type: string
          required:
            - name
        table:
          type: object
          properties:
            schema:
              type: string
            name:
              type: string
          required:
            - schema
            - name
        created_at:
          type: string
      required:
        - id
        - event
        - created_at
    Error:
      type: object
      additionalProperties: true
      properties:
        name:
          type: string
        message:
          type: string
        errors:
          type: array
          items:
            type: object
            properties:
              path:
                type: string
              message:
                type: string

    InventoryBulkRequestItem:
      type: object
      required:
        - showroom_ids
        - vehicle
        - assets
      properties:
        showroom_ids:
          description: An array of the Vyro Showroom's to associate this vehicle with. The client must have access to all of the showroom's listed.
          type: array
          items:
            type: string
            format: uuid
        vehicle:
          $ref: "#/components/schemas/Vehicle"
        location:
          $ref: "#/components/schemas/Location"
        handover_locations:
          type: array
          items:
            $ref: "#/components/schemas/Location"
        assets:
          type: array
          items:
            $ref: "#/components/schemas/Asset"

    Address:
      type: object
      nullable: true
      properties:
        address_line_1:
          type: string
          nullable: true
        address_line_2:
          type: string
          nullable: true
        address_line_3:
          type: string
          nullable: true
        version:
          type: string
          enum:
            - v2
          nullable: true
        city:
          type: string
          nullable: true
        country_code:
          type: string
          description: The ISO 3166-1 alpha-2 country code. E.g. AU, NZ, US, GB, etc.
          pattern: "^[A-Z]{2}$"
          nullable: true
        country:
          type: string
          nullable: true
        formatted_address:
          description: A human-readable address. If this is not provided, it will be generated from the other address fields.
          type: string
          nullable: true
        google_place_id:
          description: The Google Place ID of the address.
          type: string
          nullable: true
        postcode:
          type: string
          nullable: true
        state:
          type: string
          nullable: true
        latitude:
          type: number
          format: float
          nullable: true
        longitude:
          type: number
          format: float
          nullable: true
        unit:
          type: string
          nullable: true
        street_number:
          type: string
          nullable: true
        street:
          type: string
          nullable: true

    Location:
      type: object
      required:
        - name
      properties:
        id:
          type: string
          format: uuid
          description: The ID of the location in Vyro. If this property is provided all other properties within the location object will be ignored.
          nullable: true
        source_id:
          type: string
          description: The ID of the vehicle in the source's system. If this vehicle is being created by another source, this should be the ID of the vehicle in that source's system. Source IDs are unique within an application.
          nullable: true
        name:
          type: string
          description: The name of the location.
        phone:
          type: string
          description: The phone number of the location in E.164 format. E.g. +61412345678
          nullable: true
        email:
          type: string
          description: The email address of the location in lowercase.
          nullable: true
        address:
          $ref: "#/components/schemas/Address"
        tags:
          type: array
          items:
            $ref: "#/components/schemas/Tag"

    Vehicle:
      type: object
      required:
        - source_id
        - production_year
        - make
        - model
        - condition
        - is_sold
        - is_published
        - is_listed
        - estimated_delivery_delay
      properties:
        source_id:
          type: string
          description: The ID of the vehicle in the source's system. If this vehicle is being created by another source, this should be the ID of the vehicle in that source's system. Source IDs are unique within an application.
        production_year:
          type: string
          example: "2024"
          pattern: '^\d{4}$'
          description: The vehicle's year of production in YYYY format as advertised by the manufacturer. Often refered to as the Make Year. This may be a different year to the build_date. For example an MY24 Polestar 2, which was actually built in late 2023, has a production_year of 2024.
        build_date:
          type: string
          format: date
          description: The vehicle's build date in YYYY-MM-DD format in the UTC timezone.
          nullable: true
        compliance_date:
          type: string
          format: date
          description: The vehicle's compliance date in YYYY-MM-DD format in the UTC timezone.
          nullable: true
        make:
          type: string
          example: Polestar
        model:
          type: string
          example: 2
        badge:
          type: string
          example: Long range Dual motor
          description: The vehicle's badge (aka variant).
          nullable: true
        series:
          type: string
          example: EFJH
          nullable: true
        make_code:
          type: string
          nullable: true
        model_code:
          type: string
          nullable: true
        badge_code:
          type: string
          nullable: true
        wheel_name:
          type: string
          nullable: true
        wheel_code:
          type: string
          nullable: true
        option_codes:
          type: array
          items:
            type: string
          nullable: true
        unmapped_option_codes:
          type: array
          items:
            type: string
          nullable: true
        pack_codes:
          type: array
          items:
            type: string
          nullable: true
        packs:
          type: string
          nullable: true
        vin:
          type: string
          nullable: true
        colour:
          type: string
          description: A name or description of the vehicle's exterior colour.
          nullable: true
        interior:
          type: string
          description: A name or description of the vehicle's interior trim.
          nullable: true
        colour_code:
          type: string
          nullable: true
        interior_code:
          type: string
          nullable: true
        stock_id:
          type: string
          description: The vehicle's stock ID. This has no material purpose other than as a tracking mechanism. Normally this is created by the source's system. If left empty, a stock_id will be automatically generated. Note, Stock IDs are unique within an application.
          nullable: true
        purchased_at:
          type: string
          format: date
          description: The date that the vehicle was purchased in YYYY-MM-DD format in the UTC timezone.
          nullable: true
        body_type:
          type: string
          description: The body type of the vehicle. E.g. Sedan, Hatchback, SUV, etc.
          nullable: true
        condition:
          type: string
          enum:
            - new
            - used
            - demo
        odometer:
          type: integer
          description: The vehicle's odometer reading in km.
          minimum: 0
        hide_odometer:
          type: boolean
          description: Whether to hide the odometer reading from the public.
          default: false
        battery_health:
          type: number
          description: The vehicle's battery health as a percentage. 100 = Perfect, 0 = Dead.
          minimum: 0
          maximum: 100
          nullable: true
        description:
          type: string
          description: The vehicle's description. Supports markdown. HTML will be sanitized. Line items will be converted to paragraphs.
          nullable: true
        specifications_provider_id:
          type: string
          description: The ID of the vehicle in the specifications_provider's system. This could be a Redbook Code, Glass Code, etc.
          nullable: true
        specifications_provider:
          type: string
          enum:
            - redbook
            - autograb
            - custom
        specifications_provider_custom_data:
          type: object
          description: Custom vehicle specification values. Used when specifications_provider is custom.
          properties:
            co2_combined:
              type: number
              nullable: true
            air_pollution_rating:
              type: number
              nullable: true
            co2_annual_emission:
              type: number
              nullable: true
            fuel_consumption_combined:
              type: number
              nullable: true
            fuel_annual_cost:
              type: number
              nullable: true
            currency_code:
              type: string
              nullable: true
            ancap_rating:
              type: number
              nullable: true
            seat_capacity:
              type: number
              nullable: true
            combined_power_kw:
              type: string
              nullable: true
            electric_engine_power_kw:
              type: number
              nullable: true
          additionalProperties: false
        specifications_provider_search_method:
          type: string
          description: The method used to search for the vehicle in the specifications_provider's system. id = specifications_provider_id, vin = vin.
          default: id
          enum:
            - id
            - vin
        state_of_registration:
          type: string
          description: The Australian state or territory that the vehicle is registered in. This is used to determine the vehicle's registration expiry date. If the vehicle is not registered in Australia, this should be left empty.
          nullable: true
        registration_number:
          type: string
          description: The vehicle's registration number.
          nullable: true
        registration_expiry_date:
          type: string
          format: date
          description: The vehicle's registration expiry date in YYYY-MM-DD format in the UTC timezone.
          nullable: true
        is_sold:
          type: boolean
          description: Whether the vehicle has been sold or not.
        is_published:
          type: boolean
          description: Controls the visibility of the vehicle's individual listing page.
        is_listed:
          type: boolean
          description: Controls the visibility of the vehicle within search results. A listed vehicle must also be published.
        estimated_delivery_delay:
          type: integer
          description: The estimated number of days that are required to deliver the vehicle from today. For example, 14 = If the vehicle is ordered today it could arrive in two weeks.
          minimum: 0
        available_from:
          type: string
          format: date
          description: The date that the vehicle will be available from in YYYY-MM-DD format in the UTC timezone.
          nullable: true
        pricing:
          $ref: "#/components/schemas/BasicPricing"
        advanced_pricing:
          $ref: "#/components/schemas/AdvancedPricing"
        vehicle_order_min_deposit:
          type: number
          description: The minimum deposit required to order the vehicle in the lowest denomination of the given currency. For AUD, this would be cents. E.g. $100 would be 10000. When not set, the deposit amount will be derived from the Showroom's settings.
          minimum: 0
          nullable: true
        make_logo_src:
          type: string
          format: uri
          description: A publicly-available absolute URL to the make's logo.
          nullable: true
        channel:
          type: string
          description: A channel that the vehicle is associated with. This is used to group vehicles together. For example, all vehicles that are part of a specific marketing campaign could be grouped together.
          nullable: true
        powered_by_logo_src:
          type: string
          format: uri
          description: A publicly-available absolute URL to the powered by logo.
          nullable: true
        powered_by_description:
          type: string
          description: A description of the powered by logo. For example "Powered by Demo Motors"
          maxLength: 60
          nullable: true
        external_cta_url:
          type: string
          format: uri
          description: A publicly-available absolute URL to an external CTA. This will be used in place of an "Order now" button on the showroom's website.
          nullable: true
        external_cta_label:
          type: string
          description: The label of the external CTA. For example "Order now"
          maxLength: 30
          nullable: true
        external_cta_description:
          type: string
          description: The description of the external CTA. For example "Continue to order on XXX website."
          maxLength: 120
          nullable: true
        transmission:
          type: string
          nullable: true
          description: The transmission details of vehicle
        engine_details:
          type: string
          nullable: true
          description: Engine details of vehicle ex. 1984cc/206kW
        useable_battery:
          type: number
          nullable: true
          description: The useable battery capacity of the vehicle in kWh.
        range:
          type: number
          nullable: true
          description: The vehicle's electric WLTP range in km.
        fuel_type:
          type: string
          nullable: true
          enum:
            - petrol
            - diesel
            - electric
            - hybrid
            - plug-in-hybrid
            - hydrogen
            - lpg
            - null
        safety_rating:
          description: The safety rating value
          type: number
          nullable: true
          minimum: 0
          maximum: 5
        fuel_rating:
          description: The fuel rating value
          type: number
          nullable: true
          minimum: 0
          maximum: 5
        derivative_code:
          type: string
          nullable: true
          description: mapped to derivative_code in stocked_vehicles
          example: "532{{field.badge}}456"
        tags:
          type: array
          items:
            $ref: "#/components/schemas/Tag"
        custom_fields:
          type: array
          items:
            $ref: "#/components/schemas/CustomField"
        features_description:
          type: string
          nullable: true
          description: A note related to features
        images_description:
          type: string
          nullable: true
          description: A general note related to vehicle images (e.g., disclaimer)
        servicing_description:
          type: string
          nullable: true
          description: A note related to the servicing
        warranty_description:
          type: string
          nullable: true
          description: A note related to the warranty
    Asset:
      type: object
      properties:
        type:
          $ref: "#/components/schemas/AssetType"
        placement:
          $ref: "#/components/schemas/AssetPlacement"
        order:
          type: integer
          minimum: 0
          description: The order of the asset within it's placement. Lower numbers appear first.
        src:
          type: string
          format: uri
          description: A publicly-available absolute URL to the asset.
        alt:
          type: string
          description: A description of the asset. Used for accessibility.
        label:
          type: string
          description: label of the asset.
        description:
          type: string
          description: Detailed description of the asset.

    Pricing:
      oneOf:
        - $ref: "#/components/schemas/BasicPricing"
        - $ref: "#/components/schemas/AdvancedPricing"

    BasicPricing:
      type: object
      required:
        - currency
        - type
        - price
      properties:
        type:
          type: string
          enum:
            - basic
        price:
          type: number
          minimum: 0
          description: The vehicle's price, including any discount, in the lowest denomination of the given currency. For AUD, this would be cents. E.g. $100 would be 10000.
        included_discount:
          type: number
          minimum: 0
          nullable: true
          description: A discount which has already been subtracted from the price, as a positive number in the lowest denomination of the given currency. For AUD, this would be cents. E.g. $100 would be 10000.
        excls_on_roads:
          type: boolean
          default: false
          description: Whether the price excludes on-road costs that may be incurred when purchasing the vehicle.
        currency:
          type: string
          enum:
            - AUD
            - EUR
            - USD
            - NZD

    AdvancedPricing:
      type: object
      required:
        - type
        - items
      properties:
        type:
          type: string
          enum:
            - advanced
        items:
          type: array
          items:
            $ref: "#/components/schemas/PricingConfig"

    PricingConfig:
      type: object
      required:
        - pricing_code
        - version
        - lineItems
        - driveAwayPrice
      properties:
        pricing_code:
          type: string
          description: The pricing code in Vyro. This is typically used to identify the region that the pricing refers to.
        version:
          type: string
          enum:
            - v2
        driveAwayPrice:
          type: number
          minimum: 0
          description: The total drive away price of the vehicle.
        isExclOnRoads:
          type: boolean
          default: false
          description: Whether the price excludes on-road costs that may be incurred when purchasing the vehicle.
        isVariable:
          type: boolean
          default: false
          description: Whether the price point is an estimate, yet to be released, or subject to application.
        lineItems:
          type: object
          required:
            - dutiable
            - onRoad
            - additional
          properties:
            dutiable:
              type: array
              items:
                $ref: "#/components/schemas/PricingLineItem"
            onRoad:
              type: array
              items:
                $ref: "#/components/schemas/PricingLineItem"
            additional:
              type: array
              items:
                $ref: "#/components/schemas/PricingLineItem"

    PricingLineItem:
      type: object
      required:
        - value
        - code
      properties:
        value:
          type: number
          description: The value of the line item in the lowest denomination of the given currency. For AUD, this would be cents. E.g. $100 would be 10000. In the case of a discount, set this to a negative value.
        description:
          type: string
          description: A description of the line item.
        code:
          type: string
          description: A code for the line item. This is used to identify the line item in the source's system.
        customerType:
          $ref: "#/components/schemas/PricingCustomerType"

    PricingCustomerType:
      type: string
      enum:
        - consumer
        - commercial

    AssetType:
      type: string
      enum:
        - image
        - video

    AssetPlacement:
      type: string
      enum:
        - featured
        - gallery
        - exterior
        - interior
        - wheel
        - packs
        - powered_by_logo

    RegistrationState:
      type: string
      enum:
        - NSW
        - VIC
        - QLD
        - SA
        - WA
        - TAS
        - NT
        - ACT

    WorkflowStep:
      type: string
      enum:
        - start
        - image
        - redbook
        - content
        - store
        - update

    Job:
      type: object
      properties:
        id:
          type: string
          format: uuid
        errors:
          type: array
          items:
            type: object
            additionalProperties: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        ttl:
          type: number

    JobItem:
      type: object
      additionalProperties: true
      required:
        - id
        - job_id
        - idx
        - source_name
      properties:
        id:
          type: string
          format: uuid
        job_id:
          type: string
          format: uuid
        idx:
          type: number
        job_status:
          $ref: "#/components/schemas/JobStatus"
        errors:
          type: array
          items:
            type: object
            additionalProperties: true
        source_name:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        data:
          $ref: "#/components/schemas/InventoryBulkRequestItem"
        ttl:
          type: number
        moderate_content:
          type: boolean
        moderate_images:
          type: boolean
        image_moderation:
          $ref: "#/components/schemas/ImageModerationResult"

    ImageModerationResult:
      type: object
      properties:
        accepted:
          type: array
          items:
            $ref: "#/components/schemas/ModeratedImage"
        rejected:
          type: array
          items:
            $ref: "#/components/schemas/ModeratedImage"

    ModeratedImage:
      type: object
      properties:
        src:
          type: string

    JobStatus:
      type: string
      enum:
        - pending
        - processing
        - completed
        - failed

    TaskStatus:
      type: string
      enum:
        - completed
        - failed
        - skipped

    TaskInputError:
      type: object
      required:
        - step
        - message
      properties:
        message:
          type: string
        step:
          $ref: "#/components/schemas/WorkflowStep"

    TaskInputTemplate:
      type: object
      required:
        - taskStatus
        - debugId
        - step
        - payload
      properties:
        debugId:
          type: string
        payload:
          $ref: "#/components/schemas/JobItem"
        taskStatus:
          $ref: "#/components/schemas/TaskStatus"
        errors:
          type: array
          items:
            $ref: "#/components/schemas/TaskInputError"
        currentStepError:
          $ref: "#/components/schemas/TaskInputError"
        step:
          $ref: "#/components/schemas/WorkflowStep"

    Tag:
      type: object
      required:
        - code
      properties:
        code:
          type: string
          description: A showroom unique code for the tag
        label:
          type: string
          description: A human readable label for the tag. This is what will be displayed to the user. If not provided, the code will be used.
        colour_scheme:
          type: string
          default: blackAlpha
          enum:
            - red
            - blue
            - green
            - yellow
            - purple
            - orange
            - blackAlpha

    CustomField:
      type: object
      required:
        - name
        - value
      properties:
        name:
          type: string
          description: A name for the custom field. If the custom field does not already exist, it will be created.
        value:
          type: string
          nullable: true
