openapi: 3.1.0
info:
  title: auto usate benissimo public discovery API
  version: 1.2.0
  summary: Ricerca read-only sulle offerte pubblicate dai concessionari.
  description: >-
    Read-only dealer inventory discovery. The portal is not a seller or broker:
    every result links to the vehicle page on the dealer's own site, and the
    dealer owns price, data and availability. No write, contact, booking or
    payment operation exists. AI usage policy:
    https://autousatebenissimo.it/ai.txt
  contact:
    name: auto usate benissimo
    url: https://autousatebenissimo.it/come-funziona
    email: team@azureautomotive.it
  license:
    name: Contenuti CC-BY-4.0, uso read-only
    identifier: CC-BY-4.0
servers:
  - url: https://autousatebenissimo.it
    description: Canonical public origin
externalDocs:
  description: Istruzioni per agenti
  url: https://autousatebenissimo.it/agents.md
tags:
  - name: discovery
    description: Interrogazione in sola lettura dell'inventario dei concessionari.
paths:
  /ask:
    get:
      tags: [discovery]
      operationId: askVehiclesGet
      summary: Embedded GET compatibility list of up to five dealer vehicles
      parameters:
        - { name: query, in: query, required: true, schema: { type: string, minLength: 2, maxLength: 80 } }
        - { name: mode, in: query, required: true, schema: { type: string, enum: [list] } }
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 5, default: 5 } }
        - { name: catalog_type, in: query, schema: { type: string, enum: [auto, vcom, vind] } }
        - { name: brand, in: query, schema: { type: string, minLength: 1, maxLength: 80 } }
        - { name: model, in: query, schema: { type: string, minLength: 1, maxLength: 80 } }
        - { name: body_type, in: query, schema: { type: string, minLength: 1, maxLength: 80 } }
        - { name: province, in: query, schema: { type: string, minLength: 1, maxLength: 80 } }
        - { name: fuel, in: query, schema: { type: string, minLength: 1, maxLength: 80 } }
        - { name: gearbox, in: query, schema: { type: string, minLength: 1, maxLength: 80 } }
        - { name: min_price, in: query, schema: { type: number, minimum: 0 } }
        - { name: max_price, in: query, schema: { type: number, minimum: 0 } }
        - { name: min_km, in: query, schema: { type: integer, minimum: 0 } }
        - { name: max_km, in: query, schema: { type: integer, minimum: 0 } }
        - { name: min_year, in: query, schema: { type: integer, minimum: 1886, maximum: 2100 } }
        - { name: max_year, in: query, schema: { type: integer, minimum: 1886, maximum: 2100 } }
      responses:
        '200':
          description: Bounded vehicle matches with validated dealer URLs
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AskListResponse' }
        '400': { description: Invalid request }
        '429': { $ref: '#/components/responses/RateLimited' }
        '503': { description: Discovery temporarily unavailable }
    post:
      tags: [discovery]
      operationId: askVehiclesPost
      summary: NLWeb v0.55 bounded conversational-search list
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [query]
              properties:
                query:
                  type: object
                  additionalProperties: false
                  required: [text]
                  properties:
                    text: { type: string, minLength: 2, maxLength: 80 }
                    catalog_type: { type: string, enum: [auto, vcom, vind] }
                    brand: { type: string, minLength: 1, maxLength: 80 }
                    model: { type: string, minLength: 1, maxLength: 80 }
                    body_type: { type: string, minLength: 1, maxLength: 80 }
                    province: { type: string, minLength: 1, maxLength: 80 }
                    fuel: { type: string, minLength: 1, maxLength: 80 }
                    gearbox: { type: string, minLength: 1, maxLength: 80 }
                    min_price: { type: number, minimum: 0 }
                    max_price: { type: number, minimum: 0 }
                    min_km: { type: integer, minimum: 0 }
                    max_km: { type: integer, minimum: 0 }
                    min_year: { type: integer, minimum: 1886, maximum: 2100 }
                    max_year: { type: integer, minimum: 1886, maximum: 2100 }
                prefer:
                  type: object
                  additionalProperties: false
                  description: Optional NLWeb preferences. Defaults select the supported list-only, non-streaming conversational-search response. Structurally valid unsupported preferences return an HTTP 200 application failure envelope.
                  properties:
                    mode: { type: string, default: list, description: Defaults to list. Only list is supported; other valid strings return UNSUPPORTED_MODE in an HTTP 200 failure envelope. }
                    streaming: { type: boolean, default: false, description: Defaults to false. true is a valid preference but is unsupported and returns UNSUPPORTED_MODE in an HTTP 200 failure envelope. }
                    response_format: { type: string, default: conversational_search, description: Defaults to conversational_search. Other valid strings return UNSUPPORTED_FORMAT in an HTTP 200 failure envelope. }
                meta:
                  type: object
                  additionalProperties: false
                  properties:
                    version: { type: string, const: '0.55' }
      responses:
        '200':
          description: NLWeb answer, or an application-level unsupported-capability failure; no matches use an answer with an empty results array
          content:
            application/json:
              schema:
                oneOf:
                  - { $ref: '#/components/schemas/NLWebAnswer' }
                  - { $ref: '#/components/schemas/NLWebFailure' }
        '400': { description: Invalid or unsupported NLWeb subset request }
        '406': { description: Accept negotiation does not permit an application/json response }
        '413': { description: Request body exceeds 8192 bytes }
        '415': { description: Content-Type must be application/json }
        '429': { $ref: '#/components/responses/RateLimited' }
        '503': { description: Discovery temporarily unavailable }
components:
  responses:
    RateLimited:
      description: Rate limited. 30 richieste al minuto per indirizzo IP (burst massimo 30 in 10 secondi), budget condiviso con la ricerca del sito.
      headers:
        Retry-After:
          required: true
          description: 30 richieste al minuto per indirizzo IP (burst massimo 30 in 10 secondi), budget condiviso con la ricerca del sito. Secondi di attesa prima di riprovare.
          schema: { type: integer, minimum: 1 }
  schemas:
    Vehicle:
      description: One dealer vehicle, as the GET binding returns it. The dealer named here is the seller and both URLs lead to the dealer's own site.
      type: object
      additionalProperties: false
      maxProperties: 20
      required: [id, title, catalog_type, dealer_name, dealer_id, dealer_vdp_url, outbound_url]
      properties:
        id: { type: string, minLength: 1, maxLength: 128 }
        title: { type: string, minLength: 1, maxLength: 240 }
        brand: { type: string, minLength: 1, maxLength: 160 }
        model: { type: string, minLength: 1, maxLength: 160 }
        price: { type: [number, 'null'], minimum: 0, maximum: 100000000 }
        currency: { type: string, const: EUR }
        year: { type: integer, minimum: 0, maximum: 2100 }
        km: { type: integer, minimum: 0, maximum: 9007199254740991 }
        fuel: { type: string, minLength: 1, maxLength: 160 }
        gearbox: { type: string, minLength: 1, maxLength: 160 }
        catalog_type: { type: string, enum: [auto, vcom, vind] }
        body_type: { type: string, minLength: 1, maxLength: 160 }
        image_url: { type: string, format: uri, pattern: '^https://', maxLength: 2048 }
        dealer_name: { type: string, minLength: 1, maxLength: 160 }
        dealer_id: { type: string, minLength: 1, maxLength: 128 }
        dealer_city: { type: string, minLength: 1, maxLength: 160 }
        dealer_province: { type: string, minLength: 1, maxLength: 160 }
        dealer_logo_url: { type: string, format: uri, pattern: '^https://', maxLength: 2048 }
        dealer_vdp_url: { type: string, format: uri, pattern: '^https://', maxLength: 2048 }
        outbound_url: { type: string, format: uri, pattern: '^https://', maxLength: 2048 }
      allOf:
        - if:
            required: [price]
            properties:
              price: { type: number }
          then:
            required: [currency]
    AskListResponse:
      type: object
      additionalProperties: false
      required: [query, mode, returned, limit, items]
      properties:
        query: { type: string, minLength: 2, maxLength: 80 }
        mode: { type: string, const: list }
        returned: { type: integer, minimum: 0, maximum: 5 }
        limit: { type: integer, minimum: 1, maximum: 5 }
        items:
          type: array
          maxItems: 5
          items:
            type: object
            additionalProperties: false
            required: [rank, match, vehicle]
            properties:
              rank: { type: integer, minimum: 1, maximum: 5 }
              match: { type: string, enum: [both, semantic, lexical] }
              vehicle: { $ref: '#/components/schemas/Vehicle' }
    NLWebVehicle:
      description: The same vehicle as a schema.org Vehicle; offers.url and offers.seller stay the dealer's.
      type: object
      additionalProperties: false
      required: ['@context', '@type', identifier, name, url, offers]
      properties:
        '@context': { type: string, const: 'https://schema.org' }
        '@type': { type: string, const: Vehicle }
        identifier: { type: string, minLength: 1, maxLength: 128 }
        name: { type: string, minLength: 1, maxLength: 240 }
        url: { type: string, format: uri, pattern: '^https://', maxLength: 2048 }
        image: { type: string, format: uri, pattern: '^https://', maxLength: 2048 }
        brand:
          type: object
          additionalProperties: false
          required: ['@type', name]
          properties:
            '@type': { type: string, const: Brand }
            name: { type: string, minLength: 1, maxLength: 160 }
        model: { type: string, maxLength: 160 }
        vehicleModelDate: { type: string, pattern: '^[0-9]{1,4}$' }
        mileageFromOdometer:
          type: object
          additionalProperties: false
          required: ['@type', value, unitCode]
          properties:
            '@type': { type: string, const: QuantitativeValue }
            value: { type: integer, minimum: 0, maximum: 9007199254740991 }
            unitCode: { type: string, const: KMT }
        fuelType: { type: string, maxLength: 160 }
        vehicleTransmission: { type: string, maxLength: 160 }
        bodyType: { type: string, maxLength: 160 }
        offers:
          type: object
          additionalProperties: false
          required: ['@type', url, seller]
          properties:
            '@type': { type: string, const: Offer }
            url: { type: string, format: uri, pattern: '^https://', maxLength: 2048 }
            price: { type: number, minimum: 0, maximum: 100000000 }
            priceCurrency: { type: string, const: EUR }
            seller:
              type: object
              additionalProperties: false
              required: ['@type', name, identifier]
              properties:
                '@type': { type: string, const: Organization }
                name: { type: string, minLength: 1, maxLength: 160 }
                identifier: { type: string, minLength: 1, maxLength: 128 }
          allOf:
            - if:
                required: [price]
              then:
                required: [priceCurrency]
    NLWebAnswer:
      type: object
      additionalProperties: false
      required: [_meta, results]
      properties:
        _meta:
          type: object
          additionalProperties: false
          required: [response_type, response_format, version]
          properties:
            response_type: { type: string, const: answer }
            response_format: { type: string, const: conversational_search }
            version: { type: string, const: '0.55' }
        results:
          type: array
          maxItems: 5
          items: { $ref: '#/components/schemas/NLWebVehicle' }
    NLWebFailure:
      type: object
      additionalProperties: false
      required: [_meta, error]
      properties:
        _meta:
          type: object
          additionalProperties: false
          required: [response_type, version]
          properties:
            response_type: { type: string, const: failure }
            version: { type: string, const: '0.55' }
        error:
          type: object
          additionalProperties: false
          required: [code, message]
          properties:
            code: { type: string, enum: [UNSUPPORTED_FORMAT, UNSUPPORTED_MODE] }
            message: { type: string, minLength: 1, maxLength: 160 }
