openapi: 3.0.3

info:
  title: SalesMate Integration API
  description: |
    **SalesMate Integration Standard v1**

    Specifica degli endpoint REST che il Tenant deve esporre per consentire
    al Chatbot AI SalesMate di accedere ai dati dell'ecommerce.

    ## Percorsi Base (Placeholder)

    I percorsi base sono definiti dal Tenant durante la fase di onboarding
    e comunicati al team SalesMate prima dell'attivazione dell'integrazione:

    | Placeholder | Descrizione |
    |---|---|
    | `{tenant-domain}` | Dominio del sito ecommerce del Tenant |
    | `{api_base}` | Percorso base degli endpoint di gestione dati (customers, orders, products) |
    | `{store_base}` | Percorso base degli endpoint operativi (carrello, checkout) |
    | `{ext_base}` | Percorso base dell'endpoint SalesMate custom (cart session) |

    > **Nota implementativa:** I percorsi base sono definiti liberamente dal Tenant
    > in base alla propria architettura. Il Tenant comunica i valori scelti al team
    > SalesMate durante l'onboarding, che li configura nel MCP Layer.

    ## Autenticazione

    Tutte le chiamate utilizzano **HTTP Basic Auth** con Consumer Key e Consumer Secret.
    La connessione deve avvenire esclusivamente su **HTTPS**.

    ## Regola Critica

    Il `customer_id` esposto dagli endpoint `/customers/{id}` **DEVE** essere lo stesso
    identificatore configurato nel token di identità del widget SalesMate. Questo è il
    punto di giunzione tra i due sistemi.

  version: "1.0.0"
  contact:
    name: Team Tecnico SalesMate
  license:
    name: Proprietario – Uso riservato al Tenant

servers:
  - url: "https://{tenant-domain}/{api_base}"
    description: "Gestione dati principale (customers, orders, products)"
    variables:
      tenant-domain:
        default: "shop.esempio.it"
        description: "Dominio del sito ecommerce del Tenant"
      api_base:
        default: "api/v1"
        description: "Percorso base comunicato dal Tenant in fase di onboarding"

  - url: "https://{tenant-domain}/{store_base}"
    description: "Operazioni store (carrello e checkout)"
    variables:
      tenant-domain:
        default: "shop.esempio.it"
        description: "Dominio del sito ecommerce del Tenant"
      store_base:
        default: "store/v1"
        description: "Percorso base store comunicato dal Tenant in fase di onboarding"

  - url: "https://{tenant-domain}/{ext_base}"
    description: "Estensioni SalesMate custom (cart session)"
    variables:
      tenant-domain:
        default: "shop.esempio.it"
        description: "Dominio del sito ecommerce del Tenant"
      ext_base:
        default: "ext/v1"
        description: "Percorso base estensioni comunicato dal Tenant in fase di onboarding"

# ─── Sicurezza globale ────────────────────────────────────────────────────────
security:
  - basicAuth: []

components:

  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
      description: |
        Consumer Key come username, Consumer Secret come password.
        Formato Consumer Key: `ck_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
        Formato Consumer Secret: `cs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`

  # ─── Parametri riutilizzabili ─────────────────────────────────────────────
  parameters:

    customerId:
      name: id
      in: path
      required: true
      description: "ID univoco del cliente nel sistema ecommerce del Tenant. Deve corrispondere all'identifier del token di identità SalesMate."
      schema:
        type: integer
        example: 123

    orderId:
      name: id
      in: path
      required: true
      description: "ID univoco dell'ordine"
      schema:
        type: integer
        example: 789

    productId:
      name: id
      in: path
      required: true
      description: "ID univoco del prodotto"
      schema:
        type: integer
        example: 456

    paramPage:
      name: page
      in: query
      required: false
      description: "Numero di pagina (default: 1)"
      schema:
        type: integer
        default: 1
        minimum: 1

    paramPerPage:
      name: per_page
      in: query
      required: false
      description: "Elementi per pagina (default: 10, massimo: 100)"
      schema:
        type: integer
        default: 10
        minimum: 1
        maximum: 100

    paramOrderby:
      name: orderby
      in: query
      required: false
      description: "Campo di ordinamento"
      schema:
        type: string
        default: date

    paramOrder:
      name: order
      in: query
      required: false
      description: "Direzione ordinamento"
      schema:
        type: string
        enum: [asc, desc]
        default: desc

    paramModifiedAfter:
      name: modified_after
      in: query
      required: false
      description: "ISO 8601. Filtra risorse modificate dopo questa data/ora. Utilizzato per sincronizzazione incrementale."
      schema:
        type: string
        format: date-time
        example: "2026-04-01T00:00:00"

    cartTokenHeader:
      name: Cart-Token
      in: header
      required: true
      description: "Token di sessione carrello ottenuto dalla chiamata POST /cart/session"
      schema:
        type: string
        example: "sm_cart_abc123xyz789"

  # ─── Risposte riutilizzabili ──────────────────────────────────────────────
  responses:

    Unauthorized:
      description: "Credenziali assenti o non valide"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ApiError"
          example:
            code: "salesmate_unauthorized"
            message: "Credenziali non valide o assenti."
            data:
              status: 401

    Forbidden:
      description: "Credenziali valide ma permessi insufficienti"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ApiError"
          example:
            code: "salesmate_forbidden"
            message: "Permessi insufficienti per questa operazione."
            data:
              status: 403

    NotFound:
      description: "Risorsa non trovata"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ApiError"
          example:
            code: "salesmate_not_found"
            message: "La risorsa richiesta non esiste."
            data:
              status: 404

    BadRequest:
      description: "Parametri mancanti o non validi"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ApiError"
          example:
            code: "salesmate_bad_request"
            message: "Parametri mancanti o non validi."
            data:
              status: 400

    UnprocessableEntity:
      description: "Dati corretti ma non processabili (es. prodotto esaurito)"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ApiError"
          example:
            code: "salesmate_product_out_of_stock"
            message: "Il prodotto richiesto non è disponibile in magazzino."
            data:
              status: 422

  # ─── Schemi ───────────────────────────────────────────────────────────────
  schemas:

    ApiError:
      type: object
      required: [code, message, data]
      properties:
        code:
          type: string
          description: "Codice errore machine-readable"
          example: "salesmate_customer_invalid_id"
        message:
          type: string
          description: "Messaggio leggibile dall'utente"
          example: "Il Customer ID fornito non esiste nel sistema."
        data:
          type: object
          properties:
            status:
              type: integer
              description: "HTTP status code"
              example: 404

    Address:
      type: object
      properties:
        first_name:
          type: string
          example: "Mario"
        last_name:
          type: string
          example: "Rossi"
        address_1:
          type: string
          example: "Via Roma 1"
        address_2:
          type: string
          example: ""
        city:
          type: string
          example: "Milano"
        postcode:
          type: string
          example: "20121"
        country:
          type: string
          description: "Codice paese ISO 3166-1 alpha-2"
          example: "IT"
        email:
          type: string
          format: email
          example: "mario.rossi@example.com"
        phone:
          type: string
          example: "+39 02 123 4567"

    Customer:
      type: object
      required: [id, email, first_name, last_name, billing, shipping]
      properties:
        id:
          type: integer
          description: "ID univoco del cliente. Deve corrispondere all'identifier del token di identità SalesMate."
          example: 123
        date_created:
          type: string
          format: date-time
          example: "2024-01-15T09:30:00"
        email:
          type: string
          format: email
          example: "mario.rossi@example.com"
        first_name:
          type: string
          example: "Mario"
        last_name:
          type: string
          example: "Rossi"
        username:
          type: string
          example: "mario.rossi"
        role:
          type: string
          example: "customer"
        billing:
          $ref: "#/components/schemas/Address"
        shipping:
          $ref: "#/components/schemas/Address"
        orders_count:
          type: integer
          example: 15
        total_spent:
          type: string
          description: "Totale speso. Stringa decimale in valuta locale."
          example: "1250.00"
        avatar_url:
          type: string
          format: uri
          example: "https://example.com/avatar/123.jpg"
        meta_data:
          type: array
          items:
            $ref: "#/components/schemas/MetaData"

    CustomerUpdate:
      type: object
      description: "Campi aggiornabili dal bot. Il Tenant deve ignorare o rifiutare campi non presenti in questo schema (email, password, username)."
      properties:
        first_name:
          type: string
        last_name:
          type: string
        billing:
          type: object
          properties:
            first_name:
              type: string
            last_name:
              type: string
            address_1:
              type: string
            address_2:
              type: string
            city:
              type: string
            postcode:
              type: string
            country:
              type: string
            phone:
              type: string
        shipping:
          type: object
          properties:
            first_name:
              type: string
            last_name:
              type: string
            address_1:
              type: string
            address_2:
              type: string
            city:
              type: string
            postcode:
              type: string
            country:
              type: string

    OrderSummary:
      type: object
      properties:
        id:
          type: integer
          example: 789
        status:
          type: string
          enum: [pending, processing, on-hold, completed, cancelled, refunded]
          example: "completed"
        date_created:
          type: string
          format: date-time
          example: "2026-03-10T14:22:00"
        date_modified:
          type: string
          format: date-time
          example: "2026-03-12T09:05:00"
        currency:
          type: string
          example: "EUR"
        total:
          type: string
          example: "149.90"
        items_count:
          type: integer
          example: 2
        billing:
          $ref: "#/components/schemas/Address"
        shipping:
          $ref: "#/components/schemas/Address"

    OrderLineItem:
      type: object
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: "Scarpa Running XZ-200"
        product_id:
          type: integer
          example: 456
        variation_id:
          type: integer
          example: 789
        quantity:
          type: integer
          example: 1
        sku:
          type: string
          example: "SKU-XZ200-42"
        subtotal:
          type: string
          example: "129.90"
        total:
          type: string
          example: "129.90"

    ShippingLine:
      type: object
      properties:
        method_title:
          type: string
          example: "BRT Corriere"
        total:
          type: string
          example: "0.00"

    TrackingItem:
      type: object
      required: [tracking_provider, tracking_number, tracking_link, date_shipped]
      properties:
        tracking_provider:
          type: string
          description: "Nome del corriere"
          example: "BRT"
        tracking_number:
          type: string
          description: "Codice di tracking assegnato dal corriere"
          example: "1234567890"
        tracking_link:
          type: string
          format: uri
          description: "Link diretto alla pagina di tracking del corriere. Deve essere un URL valido e accessibile pubblicamente."
          example: "https://www.brt.it/tracking?id=1234567890"
        date_shipped:
          type: string
          format: date
          description: "Data di spedizione (YYYY-MM-DD)"
          example: "2026-03-29"

    MetaData:
      type: object
      properties:
        key:
          type: string
          example: "_wc_shipment_tracking_items"
        value:
          description: "Valore del metadato. Per il tracking spedizione deve essere un array di TrackingItem."
          oneOf:
            - type: string
            - type: array
              items:
                $ref: "#/components/schemas/TrackingItem"

    Order:
      type: object
      properties:
        id:
          type: integer
          example: 789
        status:
          type: string
          enum: [pending, processing, on-hold, completed, cancelled, refunded]
          example: "processing"
        date_created:
          type: string
          format: date-time
          example: "2026-03-28T14:22:00"
        date_modified:
          type: string
          format: date-time
          example: "2026-03-29T10:05:00"
        currency:
          type: string
          example: "EUR"
        total:
          type: string
          example: "149.90"
        subtotal:
          type: string
          example: "129.90"
        total_tax:
          type: string
          example: "0.00"
        shipping_total:
          type: string
          example: "0.00"
        line_items:
          type: array
          items:
            $ref: "#/components/schemas/OrderLineItem"
        shipping_lines:
          type: array
          items:
            $ref: "#/components/schemas/ShippingLine"
        billing:
          $ref: "#/components/schemas/Address"
        shipping:
          $ref: "#/components/schemas/Address"
        payment_method_title:
          type: string
          example: "Carta di Credito"
        customer_note:
          type: string
          example: ""
        meta_data:
          type: array
          description: "Deve includere la chiave _wc_shipment_tracking_items quando il tracking è disponibile."
          items:
            $ref: "#/components/schemas/MetaData"

    ProductCategory:
      type: object
      properties:
        id:
          type: integer
          example: 12
        name:
          type: string
          example: "Scarpe Running"
        slug:
          type: string
          example: "scarpe-running"

    ProductImage:
      type: object
      properties:
        id:
          type: integer
          example: 100
        src:
          type: string
          format: uri
          example: "https://shop.esempio.it/images/product-456.jpg"
        alt:
          type: string
          example: "Scarpa Running XZ-200 colore Blu"

    ProductSummary:
      type: object
      required: [id, name, price, stock_status]
      properties:
        id:
          type: integer
          example: 456
        name:
          type: string
          example: "Scarpa Running XZ-200"
        sku:
          type: string
          example: "SKU-XZ200"
        type:
          type: string
          enum: [simple, variable, grouped, external]
          example: "variable"
        price:
          type: string
          description: "Prezzo attivo (già scontato se in promozione). Stringa decimale."
          example: "129.90"
        regular_price:
          type: string
          example: "149.90"
        sale_price:
          type: string
          description: "Prezzo in offerta se attiva, stringa vuota altrimenti."
          example: "129.90"
        stock_status:
          type: string
          enum: [instock, outofstock, onbackorder]
          example: "instock"
        stock_quantity:
          type: integer
          nullable: true
          description: "Null se il conteggio scorte non è abilitato."
          example: 42
        categories:
          type: array
          items:
            $ref: "#/components/schemas/ProductCategory"
        images:
          type: array
          items:
            $ref: "#/components/schemas/ProductImage"

    Product:
      allOf:
        - $ref: "#/components/schemas/ProductSummary"
        - type: object
          properties:
            short_description:
              type: string
              description: "Descrizione breve. Può contenere HTML."
              example: "<p>Scarpa da running leggera e reattiva.</p>"
            description:
              type: string
              description: "Descrizione completa. Può contenere HTML."
            variations:
              type: array
              description: "Lista ID varianti (solo per type: variable). Usare con GET /products/{id}/variations."
              items:
                type: integer
              example: [101, 102, 103]

    ProductVariation:
      type: object
      required: [id, price, stock_status]
      properties:
        id:
          type: integer
          example: 101
        sku:
          type: string
          example: "SKU-XZ200-42-BLU"
        price:
          type: string
          example: "129.90"
        regular_price:
          type: string
          example: "149.90"
        sale_price:
          type: string
          example: "129.90"
        stock_status:
          type: string
          enum: [instock, outofstock]
          example: "instock"
        stock_quantity:
          type: integer
          nullable: true
          example: 10
        attributes:
          type: array
          description: "Attributi che identificano questa variante"
          items:
            type: object
            properties:
              name:
                type: string
                example: "Colore"
              option:
                type: string
                example: "Blu"

    Category:
      type: object
      required: [id, name, slug]
      properties:
        id:
          type: integer
          example: 12
        name:
          type: string
          example: "Scarpe Running"
        slug:
          type: string
          example: "scarpe-running"
        parent:
          type: integer
          description: "ID categoria padre. 0 se categoria radice."
          example: 0
        count:
          type: integer
          description: "Numero di prodotti nella categoria."
          example: 38

    CartSessionRequest:
      type: object
      required: [customer_id]
      properties:
        customer_id:
          type: integer
          description: "ID del cliente nel sistema ecommerce del Tenant."
          example: 123

    CartSessionResponse:
      type: object
      required: [cart_token, expires_at, customer_id]
      properties:
        cart_token:
          type: string
          description: "Token di sessione carrello. Da includere come header Cart-Token in tutte le chiamate successive agli endpoint del carrello."
          example: "sm_cart_abc123xyz789"
        expires_at:
          type: string
          format: date-time
          description: "Scadenza del token. Minimo 2 ore dalla data di emissione."
          example: "2026-04-01T15:00:00"
        customer_id:
          type: integer
          example: 123

    CartPrices:
      type: object
      properties:
        price:
          type: string
          description: "Prezzo in unità minori della valuta (es. 12990 = EUR 129.90 con currency_minor_unit: 2)"
          example: "12990"
        regular_price:
          type: string
          example: "14990"
        currency_code:
          type: string
          example: "EUR"
        currency_minor_unit:
          type: integer
          description: "Numero di decimali della valuta (2 per EUR)"
          example: 2

    CartVariation:
      type: object
      properties:
        attribute:
          type: string
          description: "Slug attributo WordPress (formato: pa_nome-attributo)"
          example: "pa_colore"
        value:
          type: string
          example: "Blu"

    CartItem:
      type: object
      properties:
        key:
          type: string
          description: "Chiave univoca dell'item nel carrello. Usata per rimuovere l'item."
          example: "item_key_abc123"
        id:
          type: integer
          description: "ID prodotto o variante"
          example: 456
        name:
          type: string
          example: "Scarpa Running XZ-200"
        quantity:
          type: integer
          example: 2
        prices:
          $ref: "#/components/schemas/CartPrices"
        variation:
          type: array
          items:
            $ref: "#/components/schemas/CartVariation"

    CartTotals:
      type: object
      properties:
        total_price:
          type: string
          description: "Totale in unità minori della valuta"
          example: "25980"
        total_shipping:
          type: string
          example: "0"
        currency_code:
          type: string
          example: "EUR"
        currency_minor_unit:
          type: integer
          example: 2

    Cart:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: "#/components/schemas/CartItem"
        items_count:
          type: integer
          example: 2
        totals:
          $ref: "#/components/schemas/CartTotals"

    AddToCartRequest:
      type: object
      required: [id, quantity]
      properties:
        id:
          type: integer
          description: "ID del prodotto o della variante da aggiungere."
          example: 456
        quantity:
          type: integer
          minimum: 1
          example: 1
        variation:
          type: array
          description: "Obbligatorio per prodotti di tipo variable."
          items:
            $ref: "#/components/schemas/CartVariation"

    RemoveFromCartRequest:
      type: object
      required: [key]
      properties:
        key:
          type: string
          description: "Chiave dell'item da rimuovere (campo key dalla risposta di GET /cart)."
          example: "item_key_abc123"

    CheckoutPaymentData:
      type: object
      properties:
        key:
          type: string
          example: "payment_method"
        value:
          type: string
          example: "stripe"

    CheckoutRequest:
      type: object
      required: [payment_method, billing_address, shipping_address]
      properties:
        payment_method:
          type: string
          description: "Identificatore del metodo di pagamento"
          example: "stripe"
        payment_data:
          type: array
          items:
            $ref: "#/components/schemas/CheckoutPaymentData"
        billing_address:
          $ref: "#/components/schemas/Address"
        shipping_address:
          $ref: "#/components/schemas/Address"

    CheckoutResponse:
      type: object
      required: [order_id, status]
      properties:
        order_id:
          type: integer
          example: 1001
        status:
          type: string
          example: "processing"
        order_key:
          type: string
          example: "sm_order_xyz123"
        total:
          type: string
          example: "149.90"
        currency:
          type: string
          example: "EUR"
        customer_note:
          type: string
          example: ""

# ═══════════════════════════════════════════════════════════════════════════════
# PATHS
# ═══════════════════════════════════════════════════════════════════════════════
paths:

  # ── Clienti ─────────────────────────────────────────────────────────────────
  /customers/{id}:
    get:
      tags: [Clienti]
      summary: Profilo cliente
      description: |
        Recupera il profilo completo del cliente identificato dall'ID fornito.
        Il parametro `id` corrisponde all'`identifier` ricevuto dal sistema di
        autenticazione del widget SalesMate.
      operationId: getCustomer
      parameters:
        - $ref: "#/components/parameters/customerId"
      responses:
        "200":
          description: Profilo cliente
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Customer"
              example:
                id: 123
                date_created: "2024-01-15T09:30:00"
                email: "mario.rossi@example.com"
                first_name: "Mario"
                last_name: "Rossi"
                username: "mario.rossi"
                role: "customer"
                billing:
                  first_name: "Mario"
                  last_name: "Rossi"
                  address_1: "Via Roma 1"
                  city: "Milano"
                  postcode: "20121"
                  country: "IT"
                  email: "mario.rossi@example.com"
                  phone: "+39 02 123 4567"
                shipping:
                  first_name: "Mario"
                  last_name: "Rossi"
                  address_1: "Via Roma 1"
                  city: "Milano"
                  postcode: "20121"
                  country: "IT"
                orders_count: 15
                total_spent: "1250.00"
                meta_data: []
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

    put:
      tags: [Clienti]
      summary: Aggiorna indirizzo cliente
      description: |
        Aggiorna i dati del cliente. Operazione di scrittura limitata:
        il bot può aggiornare solo i campi di indirizzo e anagrafica base.
        **Il Tenant deve ignorare o rifiutare** eventuali campi sensibili
        non previsti (email, password, username).

        > Il bot richiede conferma esplicita all'utente prima di eseguire
        > qualsiasi aggiornamento al profilo.
      operationId: updateCustomer
      parameters:
        - $ref: "#/components/parameters/customerId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomerUpdate"
            example:
              shipping:
                address_1: "Via Milano 42"
                city: "Roma"
                postcode: "00100"
                country: "IT"
      responses:
        "200":
          description: Cliente aggiornato
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Customer"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  # ── Ordini ───────────────────────────────────────────────────────────────────
  /orders:
    get:
      tags: [Ordini]
      summary: Lista ordini cliente
      description: |
        Restituisce la lista degli ordini di un cliente con supporto a filtri
        e paginazione. Il parametro `customer` è obbligatorio: il MCP Layer lo
        valorizza sempre con il Customer ID dell'utente in chat.
      operationId: listOrders
      parameters:
        - name: customer
          in: query
          required: true
          description: "ID del cliente. Obbligatorio."
          schema:
            type: integer
            example: 123
        - name: status
          in: query
          required: false
          description: "Filtra per stato ordine"
          schema:
            type: string
            enum: [all, pending, processing, on-hold, completed, cancelled, refunded]
        - $ref: "#/components/parameters/paramPage"
        - $ref: "#/components/parameters/paramPerPage"
        - $ref: "#/components/parameters/paramOrderby"
        - $ref: "#/components/parameters/paramOrder"
        - $ref: "#/components/parameters/paramModifiedAfter"
      responses:
        "200":
          description: Lista ordini
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/OrderSummary"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /orders/{id}:
    get:
      tags: [Ordini]
      summary: Dettaglio ordine
      description: |
        Restituisce il dettaglio completo di un singolo ordine, incluse le
        righe prodotto, il metodo di spedizione e — quando disponibile —
        le informazioni di tracking del corriere nel campo `meta_data`.

        **Tracking spedizione:** il campo `meta_data` deve contenere una voce
        con `key: "_wc_shipment_tracking_items"` valorizzata con l'array di
        spedizioni quando il tracking è disponibile.
      operationId: getOrder
      parameters:
        - $ref: "#/components/parameters/orderId"
      responses:
        "200":
          description: Dettaglio ordine
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Order"
              example:
                id: 789
                status: "processing"
                date_created: "2026-03-28T14:22:00"
                currency: "EUR"
                total: "149.90"
                line_items:
                  - id: 1
                    name: "Scarpa Running XZ-200"
                    product_id: 456
                    variation_id: 101
                    quantity: 1
                    sku: "SKU-XZ200-42-BLU"
                    subtotal: "129.90"
                    total: "129.90"
                shipping_lines:
                  - method_title: "BRT Corriere"
                    total: "0.00"
                payment_method_title: "Carta di Credito"
                meta_data:
                  - key: "_wc_shipment_tracking_items"
                    value:
                      - tracking_provider: "BRT"
                        tracking_number: "1234567890"
                        tracking_link: "https://www.brt.it/tracking?id=1234567890"
                        date_shipped: "2026-03-29"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  # ── Prodotti ─────────────────────────────────────────────────────────────────
  /products:
    get:
      tags: [Catalogo]
      summary: Lista prodotti
      description: |
        Restituisce la lista dei prodotti del catalogo con supporto a ricerca,
        filtri e paginazione. Usato dal bot per rispondere a ricerche di prodotto
        e per sincronizzazione incrementale tramite il parametro `modified_after`.
      operationId: listProducts
      parameters:
        - name: search
          in: query
          required: false
          description: "Ricerca testuale per nome o SKU prodotto. Ricerca parziale supportata."
          schema:
            type: string
            example: "scarpa running"
        - name: category
          in: query
          required: false
          description: "ID categoria. Filtra i prodotti appartenenti alla categoria specificata."
          schema:
            type: integer
            example: 12
        - name: status
          in: query
          required: false
          description: "Filtra per stato pubblicazione. Usare `publish` per i prodotti visibili ai clienti."
          schema:
            type: string
            default: publish
        - name: stock_status
          in: query
          required: false
          description: "Filtra per disponibilità"
          schema:
            type: string
            enum: [instock, outofstock, onbackorder]
        - $ref: "#/components/parameters/paramModifiedAfter"
        - $ref: "#/components/parameters/paramPage"
        - $ref: "#/components/parameters/paramPerPage"
      responses:
        "200":
          description: Lista prodotti
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ProductSummary"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /products/{id}:
    get:
      tags: [Catalogo]
      summary: Dettaglio prodotto
      description: |
        Restituisce il dettaglio completo di un singolo prodotto. Per i prodotti
        di tipo `variable` il campo `variations` contiene la lista degli ID delle
        varianti: usare `GET /products/{id}/variations` per i dettagli di ciascuna.
      operationId: getProduct
      parameters:
        - $ref: "#/components/parameters/productId"
      responses:
        "200":
          description: Dettaglio prodotto
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Product"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /products/{id}/variations:
    get:
      tags: [Catalogo]
      summary: Varianti prodotto
      description: |
        Restituisce le varianti di un prodotto (es. taglie, colori).
        Endpoint separato rispetto al dettaglio prodotto, richiamato dal bot
        quando `type: variable`.

        Il bot segue sempre il flusso in due step:
        1. `GET /products/{id}` → verifica `type` e recupera lista ID varianti
        2. `GET /products/{id}/variations` → dettagli di ciascuna variante
      operationId: getProductVariations
      parameters:
        - $ref: "#/components/parameters/productId"
        - $ref: "#/components/parameters/paramPage"
        - $ref: "#/components/parameters/paramPerPage"
      responses:
        "200":
          description: Lista varianti
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ProductVariation"
              example:
                - id: 101
                  sku: "SKU-XZ200-42-BLU"
                  price: "129.90"
                  regular_price: "149.90"
                  sale_price: "129.90"
                  stock_status: "instock"
                  stock_quantity: 10
                  attributes:
                    - name: "Colore"
                      option: "Blu"
                    - name: "Taglia"
                      option: "42"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /products/categories:
    get:
      tags: [Catalogo]
      summary: Categorie catalogo
      description: |
        Restituisce le categorie del catalogo. Usato dal bot per la navigazione
        per categoria e per presentare all'utente la struttura del catalogo.
      operationId: listCategories
      parameters:
        - $ref: "#/components/parameters/paramPage"
        - $ref: "#/components/parameters/paramPerPage"
      responses:
        "200":
          description: Lista categorie
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Category"
              example:
                - id: 12
                  name: "Scarpe Running"
                  slug: "scarpe-running"
                  parent: 0
                  count: 38
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ── Carrello (ext_base) ───────────────────────────────────────────────────────
  /cart/session:
    post:
      tags: [Carrello]
      summary: Crea o recupera sessione carrello
      description: |
        Crea o recupera una sessione carrello per un cliente autenticato.
        Restituisce il `cart_token` da includere come header `Cart-Token`
        in tutte le successive chiamate agli endpoint del carrello.

        **Idempotenza:** chiamate successive con lo stesso `customer_id`
        devono restituire il token esistente (se non scaduto) senza creare
        sessioni duplicate.

        **Scadenza minima:** il token deve avere una scadenza di almeno
        2 ore dalla data di emissione.

        > Questo è l'unico endpoint custom richiesto dalla specifica SalesMate.
        > Il server di questo endpoint è `{ext_base}`.
      operationId: createCartSession
      servers:
        - url: "https://{tenant-domain}/{ext_base}"
          variables:
            tenant-domain:
              default: "shop.esempio.it"
            ext_base:
              default: "ext/v1"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CartSessionRequest"
            example:
              customer_id: 123
      responses:
        "200":
          description: Sessione carrello creata o recuperata
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CartSessionResponse"
              example:
                cart_token: "sm_cart_abc123xyz789"
                expires_at: "2026-04-01T15:00:00"
                customer_id: 123
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  # ── Carrello (store_base) ─────────────────────────────────────────────────────
  /cart:
    get:
      tags: [Carrello]
      summary: Visualizza carrello
      description: |
        Restituisce il contenuto del carrello della sessione corrente.

        > Il server di questo endpoint è `{store_base}`.

        **Nota prezzi:** i prezzi sono espressi in unità minori della valuta
        (es. 12990 = EUR 129,90 con `currency_minor_unit: 2`).
        Il MCP Layer gestisce automaticamente la conversione.
      operationId: getCart
      servers:
        - url: "https://{tenant-domain}/{store_base}"
          variables:
            tenant-domain:
              default: "shop.esempio.it"
            store_base:
              default: "store/v1"
      parameters:
        - $ref: "#/components/parameters/cartTokenHeader"
      responses:
        "200":
          description: Contenuto carrello
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Cart"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /cart/add-item:
    post:
      tags: [Carrello]
      summary: Aggiunge prodotto al carrello
      description: |
        Aggiunge un prodotto al carrello della sessione corrente.

        > Il server di questo endpoint è `{store_base}`.

        Il campo `variation` è obbligatorio per i prodotti di tipo `variable`.
        Gli attributi usano il formato slug (es. `pa_colore`, `pa_taglia`).
      operationId: addToCart
      servers:
        - url: "https://{tenant-domain}/{store_base}"
          variables:
            tenant-domain:
              default: "shop.esempio.it"
            store_base:
              default: "store/v1"
      parameters:
        - $ref: "#/components/parameters/cartTokenHeader"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AddToCartRequest"
            example:
              id: 456
              quantity: 1
              variation:
                - attribute: "pa_colore"
                  value: "Blu"
                - attribute: "pa_taglia"
                  value: "42"
      responses:
        "200":
          description: Carrello aggiornato
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Cart"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/UnprocessableEntity"

  /cart/remove-item:
    delete:
      tags: [Carrello]
      summary: Rimuove prodotto dal carrello
      description: |
        Rimuove un prodotto dal carrello tramite la chiave item.
        La chiave (`key`) è presente nella risposta di `GET /cart`.

        > Il server di questo endpoint è `{store_base}`.
      operationId: removeFromCart
      servers:
        - url: "https://{tenant-domain}/{store_base}"
          variables:
            tenant-domain:
              default: "shop.esempio.it"
            store_base:
              default: "store/v1"
      parameters:
        - $ref: "#/components/parameters/cartTokenHeader"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RemoveFromCartRequest"
            example:
              key: "item_key_abc123"
      responses:
        "200":
          description: Carrello aggiornato dopo rimozione
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Cart"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /checkout:
    post:
      tags: [Carrello]
      summary: Finalizza ordine
      description: |
        Finalizza l'ordine utilizzando il contenuto del carrello corrente.
        Crea un ordine nel sistema del Tenant e svuota il carrello.

        > Il server di questo endpoint è `{store_base}`.

        **⚠️ OPERAZIONE IRREVERSIBILE.** Questo endpoint crea un ordine reale.
        Il bot richiede doppia conferma esplicita all'utente prima di chiamarlo.
        Il Tenant non deve implementare ulteriori meccanismi di conferma lato API.
      operationId: checkout
      servers:
        - url: "https://{tenant-domain}/{store_base}"
          variables:
            tenant-domain:
              default: "shop.esempio.it"
            store_base:
              default: "store/v1"
      parameters:
        - $ref: "#/components/parameters/cartTokenHeader"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CheckoutRequest"
            example:
              payment_method: "stripe"
              payment_data:
                - key: "payment_method"
                  value: "stripe"
              billing_address:
                first_name: "Mario"
                last_name: "Rossi"
                address_1: "Via Roma 1"
                city: "Milano"
                postcode: "20121"
                country: "IT"
                email: "mario@example.com"
                phone: "+39 02 123 4567"
              shipping_address:
                first_name: "Mario"
                last_name: "Rossi"
                address_1: "Via Roma 1"
                city: "Milano"
                postcode: "20121"
                country: "IT"
      responses:
        "201":
          description: Ordine creato con successo
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckoutResponse"
              example:
                order_id: 1001
                status: "processing"
                order_key: "sm_order_xyz123"
                total: "149.90"
                currency: "EUR"
                customer_note: ""
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "422":
          $ref: "#/components/responses/UnprocessableEntity"

# ─── Tag con descrizioni ──────────────────────────────────────────────────────
tags:
  - name: Clienti
    description: "Profilo utente e aggiornamento indirizzi. Server: `{api_base}`"
  - name: Ordini
    description: "Cronologia ordini e stato spedizione. Server: `{api_base}`"
  - name: Catalogo
    description: "Prodotti, varianti e categorie. Server: `{api_base}`"
  - name: Carrello
    description: |
      Gestione carrello e checkout.
      - Cart session: server `{ext_base}`
      - Operazioni carrello e checkout: server `{store_base}`
